Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4d9fe05

Browse files
authored
feat: add awsiamrds db auth driver (#12566)
1 parent 0d86dca commit 4d9fe05

26 files changed

+400
-62
lines changed

cli/server.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ import (
6464
"github.com/coder/coder/v2/coderd/autobuild"
6565
"github.com/coder/coder/v2/coderd/batchstats"
6666
"github.com/coder/coder/v2/coderd/database"
67+
"github.com/coder/coder/v2/coderd/database/awsiamrds"
6768
"github.com/coder/coder/v2/coderd/database/dbmem"
6869
"github.com/coder/coder/v2/coderd/database/dbmetrics"
6970
"github.com/coder/coder/v2/coderd/database/dbpurge"
@@ -668,12 +669,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
668669
options.Database = dbmem.New()
669670
options.Pubsub = pubsub.NewInMemory()
670671
} else {
671-
dbURL, err := escapePostgresURLUserInfo(vals.PostgresURL.String())
672-
if err != nil {
673-
return xerrors.Errorf("escaping postgres URL: %w", err)
674-
}
675-
676-
sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, dbURL)
672+
sqlDB, dbURL, err := getPostgresDB(ctx, logger, vals.PostgresURL.String(), codersdk.PostgresAuth(vals.PostgresAuth), sqlDriver)
677673
if err != nil {
678674
return xerrors.Errorf("connect to postgres: %w", err)
679675
}
@@ -2556,3 +2552,24 @@ func signalNotifyContext(ctx context.Context, inv *serpent.Invocation, sig ...os
25562552
}
25572553
return inv.SignalNotifyContext(ctx, sig...)
25582554
}
2555+
2556+
func getPostgresDB(ctx context.Context, logger slog.Logger, postgresURL string, auth codersdk.PostgresAuth, sqlDriver string) (*sql.DB, string, error) {
2557+
dbURL, err := escapePostgresURLUserInfo(postgresURL)
2558+
if err != nil {
2559+
return nil, "", xerrors.Errorf("escaping postgres URL: %w", err)
2560+
}
2561+
2562+
if auth == codersdk.PostgresAuthAWSIAMRDS {
2563+
sqlDriver, err = awsiamrds.Register(ctx, sqlDriver)
2564+
if err != nil {
2565+
return nil, "", xerrors.Errorf("register aws rds iam auth: %w", err)
2566+
}
2567+
}
2568+
2569+
sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, dbURL)
2570+
if err != nil {
2571+
return nil, "", xerrors.Errorf("connect to postgres: %w", err)
2572+
}
2573+
2574+
return sqlDB, dbURL, nil
2575+
}

cli/server_createadminuser.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"cdr.dev/slog/sloggers/sloghuman"
1414
"github.com/coder/coder/v2/cli/cliui"
1515
"github.com/coder/coder/v2/coderd/database"
16+
"github.com/coder/coder/v2/coderd/database/awsiamrds"
1617
"github.com/coder/coder/v2/coderd/database/dbtime"
1718
"github.com/coder/coder/v2/coderd/gitsshkey"
1819
"github.com/coder/coder/v2/coderd/httpapi"
@@ -25,6 +26,7 @@ import (
2526
func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
2627
var (
2728
newUserDBURL string
29+
newUserPgAuth string
2830
newUserSSHKeygenAlgorithm string
2931
newUserUsername string
3032
newUserEmail string
@@ -62,7 +64,15 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
6264
newUserDBURL = url
6365
}
6466

65-
sqlDB, err := ConnectToPostgres(ctx, logger, "postgres", newUserDBURL)
67+
sqlDriver := "postgres"
68+
if codersdk.PostgresAuth(newUserPgAuth) == codersdk.PostgresAuthAWSIAMRDS {
69+
sqlDriver, err = awsiamrds.Register(inv.Context(), sqlDriver)
70+
if err != nil {
71+
return xerrors.Errorf("register aws rds iam auth: %w", err)
72+
}
73+
}
74+
75+
sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, newUserDBURL)
6676
if err != nil {
6777
return xerrors.Errorf("connect to postgres: %w", err)
6878
}
@@ -243,6 +253,14 @@ func (r *RootCmd) newCreateAdminUserCommand() *serpent.Command {
243253
Description: "URL of a PostgreSQL database. If empty, the built-in PostgreSQL deployment will be used (Coder must not be already running in this case).",
244254
Value: serpent.StringOf(&newUserDBURL),
245255
},
256+
serpent.Option{
257+
Name: "Postgres Connection Auth",
258+
Description: "Type of auth to use when connecting to postgres.",
259+
Flag: "postgres-connection-auth",
260+
Env: "CODER_PG_CONNECTION_AUTH",
261+
Default: "password",
262+
Value: serpent.EnumOf(&newUserPgAuth, codersdk.PostgresAuthDrivers...),
263+
},
246264
serpent.Option{
247265
Env: "CODER_SSH_KEYGEN_ALGORITHM",
248266
Flag: "ssh-keygen-algorithm",

cli/testdata/coder_server_--help.golden

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ OPTIONS:
4444
Separate multiple experiments with commas, or enter '*' to opt-in to
4545
all available experiments.
4646

47+
--postgres-auth password|awsiamrds, $CODER_PG_AUTH (default: password)
48+
Type of auth to use when connecting to postgres.
49+
4750
--postgres-url string, $CODER_PG_CONNECTION_URL
4851
URL of a PostgreSQL database. If empty, PostgreSQL binaries will be
4952
downloaded from Maven (https://repo1.maven.org/maven2) and store all

cli/testdata/coder_server_create-admin-user_--help.golden

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ USAGE:
77
it to every organization.
88

99
OPTIONS:
10+
--postgres-connection-auth password|awsiamrds, $CODER_PG_CONNECTION_AUTH (default: password)
11+
Type of auth to use when connecting to postgres.
12+
1013
--email string, $CODER_EMAIL
1114
The email of the new user. If not specified, you will be prompted via
1215
stdin.

cli/testdata/server-config.yaml.golden

+3
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@ cacheDir: [cache dir]
411411
# Controls whether data will be stored in an in-memory database.
412412
# (default: <unset>, type: bool)
413413
inMemoryDatabase: false
414+
# Type of auth to use when connecting to postgres.
415+
# (default: password, type: enum[password\|awsiamrds])
416+
pgAuth: password
414417
# The algorithm to use for generating ssh keys. Accepted values are "ed25519",
415418
# "ecdsa", or "rsa4096".
416419
# (default: ed25519, type: string)

coderd/apidoc/docs.go

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package awsiamrds
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"database/sql/driver"
7+
"fmt"
8+
"net/url"
9+
10+
"github.com/aws/aws-sdk-go-v2/aws"
11+
"github.com/aws/aws-sdk-go-v2/config"
12+
"github.com/aws/aws-sdk-go-v2/feature/rds/auth"
13+
"golang.org/x/xerrors"
14+
)
15+
16+
type awsIamRdsDriver struct {
17+
parent driver.Driver
18+
cfg aws.Config
19+
}
20+
21+
var _ driver.Driver = &awsIamRdsDriver{}
22+
23+
// Register initializes and registers our aws iam rds wrapped database driver.
24+
func Register(ctx context.Context, parentName string) (string, error) {
25+
cfg, err := config.LoadDefaultConfig(ctx)
26+
if err != nil {
27+
return "", err
28+
}
29+
30+
db, err := sql.Open(parentName, "")
31+
if err != nil {
32+
return "", err
33+
}
34+
35+
// create a new aws iam rds driver
36+
d := newDriver(db.Driver(), cfg)
37+
name := fmt.Sprintf("%s-awsiamrds", parentName)
38+
sql.Register(fmt.Sprintf("%s-awsiamrds", parentName), d)
39+
40+
return name, nil
41+
}
42+
43+
// newDriver will create a new *AwsIamRdsDriver using the environment aws session.
44+
func newDriver(parentDriver driver.Driver, cfg aws.Config) *awsIamRdsDriver {
45+
return &awsIamRdsDriver{
46+
parent: parentDriver,
47+
cfg: cfg,
48+
}
49+
}
50+
51+
// Open creates a new connection to the database using the provided name.
52+
func (d *awsIamRdsDriver) Open(name string) (driver.Conn, error) {
53+
// set password with signed aws authentication token for the rds instance
54+
nURL, err := getAuthenticatedURL(d.cfg, name)
55+
if err != nil {
56+
return nil, xerrors.Errorf("assigning authentication token to url: %w", err)
57+
}
58+
59+
// make connection
60+
conn, err := d.parent.Open(nURL)
61+
if err != nil {
62+
return nil, xerrors.Errorf("opening connection with %s: %w", nURL, err)
63+
}
64+
65+
return conn, nil
66+
}
67+
68+
func getAuthenticatedURL(cfg aws.Config, dbURL string) (string, error) {
69+
nURL, err := url.Parse(dbURL)
70+
if err != nil {
71+
return "", xerrors.Errorf("parsing dbURL: %w", err)
72+
}
73+
74+
// generate a new rds session auth tokenized URL
75+
rdsEndpoint := fmt.Sprintf("%s:%s", nURL.Hostname(), nURL.Port())
76+
token, err := auth.BuildAuthToken(context.Background(), rdsEndpoint, cfg.Region, nURL.User.Username(), cfg.Credentials)
77+
if err != nil {
78+
return "", xerrors.Errorf("building rds auth token: %w", err)
79+
}
80+
// set token as user password
81+
nURL.User = url.UserPassword(nURL.User.Username(), token)
82+
83+
return nURL.String(), nil
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package awsiamrds_test
2+
3+
import (
4+
"context"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"cdr.dev/slog/sloggers/slogtest"
11+
12+
"github.com/coder/coder/v2/cli"
13+
awsrdsiam "github.com/coder/coder/v2/coderd/database/awsiamrds"
14+
"github.com/coder/coder/v2/testutil"
15+
)
16+
17+
func TestDriver(t *testing.T) {
18+
t.Parallel()
19+
// Be sure to set AWS_DEFAULT_REGION to the database region as well.
20+
// Example:
21+
// export AWS_DEFAULT_REGION=us-east-2;
22+
// export DBAWSIAMRDS_TEST_URL="postgres://user@host:5432/dbname";
23+
url := os.Getenv("DBAWSIAMRDS_TEST_URL")
24+
if url == "" {
25+
t.Skip()
26+
}
27+
28+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
29+
defer cancel()
30+
31+
sqlDriver, err := awsrdsiam.Register(ctx, "postgres")
32+
require.NoError(t, err)
33+
34+
db, err := cli.ConnectToPostgres(ctx, slogtest.Make(t, nil), sqlDriver, url)
35+
require.NoError(t, err)
36+
defer func() {
37+
_ = db.Close()
38+
}()
39+
40+
i, err := db.QueryContext(ctx, "select 1;")
41+
require.NoError(t, err)
42+
defer func() {
43+
_ = i.Close()
44+
}()
45+
46+
require.True(t, i.Next())
47+
var one int
48+
require.NoError(t, i.Scan(&one))
49+
require.Equal(t, 1, one)
50+
}

codersdk/deployment.go

+22
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ func (c *Client) Entitlements(ctx context.Context) (Entitlements, error) {
135135
return ent, json.NewDecoder(res.Body).Decode(&ent)
136136
}
137137

138+
type PostgresAuth string
139+
140+
const (
141+
PostgresAuthPassword PostgresAuth = "password"
142+
PostgresAuthAWSIAMRDS PostgresAuth = "awsiamrds"
143+
)
144+
145+
var PostgresAuthDrivers = []string{
146+
string(PostgresAuthPassword),
147+
string(PostgresAuthAWSIAMRDS),
148+
}
149+
138150
// DeploymentValues is the central configuration values the coder server.
139151
type DeploymentValues struct {
140152
Verbose serpent.Bool `json:"verbose,omitempty"`
@@ -154,6 +166,7 @@ type DeploymentValues struct {
154166
CacheDir serpent.String `json:"cache_directory,omitempty" typescript:",notnull"`
155167
InMemoryDatabase serpent.Bool `json:"in_memory_database,omitempty" typescript:",notnull"`
156168
PostgresURL serpent.String `json:"pg_connection_url,omitempty" typescript:",notnull"`
169+
PostgresAuth string `json:"pg_auth,omitempty" typescript:",notnull"`
157170
OAuth2 OAuth2Config `json:"oauth2,omitempty" typescript:",notnull"`
158171
OIDC OIDCConfig `json:"oidc,omitempty" typescript:",notnull"`
159172
Telemetry TelemetryConfig `json:"telemetry,omitempty" typescript:",notnull"`
@@ -1630,6 +1643,15 @@ when required by your organization's security policy.`,
16301643
Annotations: serpent.Annotations{}.Mark(annotationSecretKey, "true"),
16311644
Value: &c.PostgresURL,
16321645
},
1646+
{
1647+
Name: "Postgres Auth",
1648+
Description: "Type of auth to use when connecting to postgres.",
1649+
Flag: "postgres-auth",
1650+
Env: "CODER_PG_AUTH",
1651+
Default: "password",
1652+
Value: serpent.EnumOf(&c.PostgresAuth, PostgresAuthDrivers...),
1653+
YAML: "pgAuth",
1654+
},
16331655
{
16341656
Name: "Secure Auth Cookie",
16351657
Description: "Controls if the 'Secure' property is set on browser session cookies.",

docs/api/general.md

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/api/schemas.md

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/server.md

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cli/server_create-admin-user.md

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)