Skip to content

Commit 04573d1

Browse files
committed
fix: refactor RDS dump, use custom certs (#156)
1 parent d94eb7a commit 04573d1

File tree

4 files changed

+11
-149
lines changed

4 files changed

+11
-149
lines changed

configs/config.example.logical_rds_iam.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,14 @@ retrieval:
146146

147147
# Optional definition of RDS data source.
148148
rdsIam:
149-
# RDS IAM policy name.
150-
iamPolicyName: rds-dblab-retrieval
151-
152149
# AWS Region.
153150
awsRegion: us-east-2
154151

155-
# AWS username.
156-
username: aws_user
157-
158152
# RDS instance Identifier.
159153
dbInstanceIdentifier: database-1
160154

161155
# Path to the SSL root certificate: https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem
162-
sslRootCert: "/tmp/rds-combined-ca-bundle.pem"
156+
sslRootCert: "/cert/rds-combined-ca-bundle.pem"
163157

164158
# Options for a partial dump.
165159
# partial:

pkg/retrieval/engine/postgres/logical/dump.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515

1616
"github.com/docker/docker/api/types"
1717
"github.com/docker/docker/api/types/container"
18-
"github.com/docker/docker/api/types/mount"
1918
"github.com/docker/docker/api/types/network"
2019
"github.com/docker/docker/client"
2120
"github.com/pkg/errors"
@@ -90,9 +89,6 @@ type dumper interface {
9089
// GetEnvVariables returns dumper environment variables.
9190
GetCmdEnvVariables() []string
9291

93-
// GetMounts returns dumper volume configurations for mounting.
94-
GetMounts() []mount.Mount
95-
9692
// SetConnectionOptions sets connection options for dumping.
9793
SetConnectionOptions(context.Context, *Connection) error
9894
}
@@ -362,7 +358,6 @@ func (d *DumpJob) buildContainerConfig(password string) *container.Config {
362358

363359
func (d *DumpJob) buildHostConfig(ctx context.Context) (*container.HostConfig, error) {
364360
hostConfig := &container.HostConfig{
365-
Mounts: d.dumper.GetMounts(),
366361
NetworkMode: d.getContainerNetworkMode(),
367362
}
368363

pkg/retrieval/engine/postgres/logical/dump_default.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ package logical
66

77
import (
88
"context"
9-
10-
"github.com/docker/docker/api/types/mount"
119
)
1210

1311
type defaultDumper struct{}
@@ -20,10 +18,6 @@ func (d *defaultDumper) GetCmdEnvVariables() []string {
2018
return []string{}
2119
}
2220

23-
func (d *defaultDumper) GetMounts() []mount.Mount {
24-
return []mount.Mount{}
25-
}
26-
2721
func (d *defaultDumper) SetConnectionOptions(_ context.Context, _ *Connection) error {
2822
return nil
2923
}

pkg/retrieval/engine/postgres/logical/dump_rds.go

Lines changed: 10 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,20 @@ package logical
66

77
import (
88
"context"
9-
"encoding/json"
109
"fmt"
1110

1211
"github.com/aws/aws-sdk-go/aws"
13-
"github.com/aws/aws-sdk-go/aws/arn"
14-
"github.com/aws/aws-sdk-go/aws/awserr"
1512
"github.com/aws/aws-sdk-go/aws/session"
1613
"github.com/aws/aws-sdk-go/service/iam"
1714
"github.com/aws/aws-sdk-go/service/rds"
1815
"github.com/aws/aws-sdk-go/service/rds/rdsutils"
19-
"github.com/docker/docker/api/types/mount"
2016
"github.com/pkg/errors"
2117

2218
"gitlab.com/postgres-ai/database-lab/pkg/log"
2319
)
2420

2521
const (
2622
sslRootCert = "/cert/ssl-combined-ca-bundle.pem"
27-
28-
// AWS service names.
29-
serviceIAM = "iam"
30-
serviceRDSDB = "rds-db"
31-
32-
// RDS policy option values.
33-
rdsDocPolicyVersion = "2012-10-17"
34-
rdsDBConnectAction = "rds-db:connect"
3523
)
3624

3725
type rdsDumper struct {
@@ -42,22 +30,9 @@ type rdsDumper struct {
4230

4331
// RDSConfig describes configuration of an RDS instance.
4432
type RDSConfig struct {
45-
IamPolicyName string `yaml:"iamPolicyName"`
46-
AWSRegion string `yaml:"awsRegion"`
47-
DBInstance string `yaml:"dbInstanceIdentifier"`
48-
Username string `yaml:"username"`
49-
SSLRootCert string `yaml:"sslRootCert"`
50-
}
51-
52-
type policyDocument struct {
53-
Version string `json:"Version"`
54-
Statement []policyStatement `json:"Statement"`
55-
}
56-
57-
type policyStatement struct {
58-
Effect string `json:"Effect"`
59-
Action []string `json:"Action"`
60-
Resource []string `json:"Resource"`
33+
AWSRegion string `yaml:"awsRegion"`
34+
DBInstance string `yaml:"dbInstanceIdentifier"`
35+
SSLRootCert string `yaml:"sslRootCert"`
6136
}
6237

6338
func newRDSDumper(rdsCfg *RDSConfig) (*rdsDumper, error) {
@@ -85,30 +60,20 @@ Set up valid environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY`)
8560

8661
// GetEnvVariables returns dumper environment variables.
8762
func (r *rdsDumper) GetCmdEnvVariables() []string {
63+
cert := sslRootCert
64+
65+
if r.rdsCfg.SSLRootCert != "" {
66+
cert = r.rdsCfg.SSLRootCert
67+
}
68+
8869
execEnvs := []string{
89-
"PGSSLROOTCERT=" + sslRootCert,
70+
"PGSSLROOTCERT=" + cert,
9071
"PGSSLMODE=verify-ca",
9172
}
9273

9374
return execEnvs
9475
}
9576

96-
// GetMounts returns dumper volume configurations for mounting.
97-
func (r *rdsDumper) GetMounts() []mount.Mount {
98-
mounts := []mount.Mount{}
99-
100-
if r.rdsCfg != nil && r.rdsCfg.SSLRootCert != "" {
101-
// TODO (akartasov): Remove mounting after the image contains a built-in certificate.
102-
mounts = append(mounts, mount.Mount{
103-
Type: mount.TypeBind,
104-
Source: r.rdsCfg.SSLRootCert,
105-
Target: sslRootCert,
106-
})
107-
}
108-
109-
return mounts
110-
}
111-
11277
// SetConnectionOptions sets connection options for dumping.
11378
func (r *rdsDumper) SetConnectionOptions(ctx context.Context, c *Connection) error {
11479
dbInstancesOutput, err := r.rdsSvc.DescribeDBInstancesWithContext(ctx, &rds.DescribeDBInstancesInput{
@@ -126,28 +91,6 @@ func (r *rdsDumper) SetConnectionOptions(ctx context.Context, c *Connection) err
12691

12792
dbInstance := dbInstancesOutput.DBInstances[0]
12893

129-
dbARN := dbInstancesOutput.DBInstances[0].DBInstanceArn
130-
parsedDBArn, err := arn.Parse(aws.StringValue(dbARN))
131-
132-
if err != nil {
133-
return errors.Wrap(err, "failed to parse a database ARN")
134-
}
135-
136-
policy, err := r.getPolicy(r.getPolicyARN(parsedDBArn).String(), parsedDBArn, dbInstance)
137-
if err != nil {
138-
return errors.Wrap(err, "failed to get a policy")
139-
}
140-
141-
_, err = r.iamSvc.AttachUserPolicy(&iam.AttachUserPolicyInput{
142-
PolicyArn: policy.Arn,
143-
UserName: aws.String(r.rdsCfg.Username),
144-
})
145-
if err != nil {
146-
return errors.Wrap(err, "failed to attach policy to a user")
147-
}
148-
149-
log.Msg(fmt.Sprintf("Policy %q has been attached to %s", aws.StringValue(policy.Arn), r.rdsCfg.Username))
150-
15194
dbAuthToken, err := rdsutils.BuildAuthToken(
15295
fmt.Sprintf("%s:%d", aws.StringValue(dbInstance.Endpoint.Address), int(aws.Int64Value(dbInstance.Endpoint.Port))),
15396
r.rdsCfg.AWSRegion,
@@ -164,67 +107,3 @@ func (r *rdsDumper) SetConnectionOptions(ctx context.Context, c *Connection) err
164107

165108
return nil
166109
}
167-
168-
func (r *rdsDumper) getPolicyARN(parsedDBArn arn.ARN) arn.ARN {
169-
policyARN := arn.ARN{
170-
Partition: parsedDBArn.Partition,
171-
Service: serviceIAM,
172-
AccountID: parsedDBArn.AccountID,
173-
Resource: "policy/" + r.rdsCfg.IamPolicyName,
174-
}
175-
176-
return policyARN
177-
}
178-
179-
func (r *rdsDumper) getPolicy(policyARN string, parsedDBArn arn.ARN, dbInstance *rds.DBInstance) (*iam.Policy, error) {
180-
policyOutput, err := r.iamSvc.GetPolicy(&iam.GetPolicyInput{
181-
PolicyArn: aws.String(policyARN),
182-
})
183-
if err == nil {
184-
return policyOutput.Policy, nil
185-
}
186-
187-
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() != iam.ErrCodeNoSuchEntityException {
188-
return nil, errors.Wrap(err, "failed to get policy")
189-
}
190-
191-
policyDocumentData, err := json.Marshal(r.buildPolicyDocument(parsedDBArn, dbInstance))
192-
if err != nil {
193-
return nil, errors.Wrap(err, "failed to marshal a policy document")
194-
}
195-
196-
createPolicyOutput, err := r.iamSvc.CreatePolicy(&iam.CreatePolicyInput{
197-
PolicyDocument: aws.String(string(policyDocumentData)),
198-
PolicyName: aws.String(r.rdsCfg.IamPolicyName),
199-
})
200-
if err != nil {
201-
return nil, errors.Wrap(err, "failed to create a policy")
202-
}
203-
204-
log.Msg(fmt.Sprintf("Policy has been created: %v", aws.StringValue(createPolicyOutput.Policy.Arn)))
205-
206-
return createPolicyOutput.Policy, nil
207-
}
208-
209-
func (r *rdsDumper) buildPolicyDocument(parsedDBArn arn.ARN, dbInstance *rds.DBInstance) policyDocument {
210-
dbPolicyARN := arn.ARN{
211-
Partition: parsedDBArn.Partition,
212-
Service: serviceRDSDB,
213-
Region: parsedDBArn.Region,
214-
AccountID: parsedDBArn.AccountID,
215-
Resource: fmt.Sprintf("dbuser:%s/*", aws.StringValue(dbInstance.DbiResourceId)),
216-
}
217-
218-
policyDocument := policyDocument{
219-
Version: rdsDocPolicyVersion,
220-
Statement: []policyStatement{
221-
{
222-
Effect: "Allow",
223-
Action: []string{rdsDBConnectAction},
224-
Resource: []string{dbPolicyARN.String()},
225-
},
226-
},
227-
}
228-
229-
return policyDocument
230-
}

0 commit comments

Comments
 (0)