diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 3a0102dd..9d72361a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -96,6 +96,7 @@ jobs: ALLOYDB_INSTANCE_NAME:${{ secrets.GOOGLE_CLOUD_PROJECT }}/ALLOYDB_INSTANCE_NAME ALLOYDB_CLUSTER_PASS:${{ secrets.GOOGLE_CLOUD_PROJECT }}/ALLOYDB_CLUSTER_PASS IMPERSONATED_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/IMPERSONATED_USER + ALLOYDB_IAM_USER:${{ secrets.GOOGLE_CLOUD_PROJECT }}/ALLOYDB_PROXY_IAM_USER - name: Run tests env: @@ -104,6 +105,7 @@ jobs: ALLOYDB_PASS: '${{ steps.secrets.outputs.ALLOYDB_CLUSTER_PASS }}' ALLOYDB_INSTANCE_NAME: '${{ steps.secrets.outputs.ALLOYDB_INSTANCE_NAME }}' IMPERSONATED_USER: '${{ steps.secrets.outputs.IMPERSONATED_USER }}' + ALLOYDB_IAM_USER: '${{ steps.secrets.outputs.ALLOYDB_IAM_USER }}' # specifying bash shell ensures a failure in a piped process isn't lost by using `set -eo pipefail` shell: bash run: | diff --git a/cmd/root.go b/cmd/root.go index 334667d1..364762ca 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -131,6 +131,25 @@ Overview hourly basis. Existing client connections are unaffected by the refresh cycle. +Authentication + + The Proxy uses Application Default Credentials by default. Enable these + credentials with gcloud: + + gcloud auth application-default login + + In Google-run environments, Application Default Credentials are already + available and do not need to be retrieved. + + The Proxy will use the environment's IAM principal when authenticating to + the backend. To use a specific set of credentials, use the + --credentials-file flag, e.g., + + ./alloydb-auth-proxy --credentials-file /path/to/key.json \ + projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE + + See the individual flags below, for more options. + Starting the Proxy To start the proxy, you will need your instance URI, which may be found in @@ -184,8 +203,29 @@ Instance Level Configuration instances, the proxy will ensure that the last path element is '.s.PGSQL.5432' appending it if necessary. For example, - ./cloud-sql-proxy \ - 'my-project:us-central1:my-db-server?unix-socket-path=/path/to/socket' + ./alloydb-auth-proxy \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE1?unix-socket-path=/path/to/socket' + + (*) indicates a flag that may be used as a query parameter + +Automatic IAM Authentication + + The Auth Proxy support Automatic IAM Authentication where the Proxy + retrieves the environment's IAM principal's OAuth2 token and supplies it to + the backend. When a client connects to the Proxy, there is no need to supply + a database user password. + + To enable the feature, run: + + ./alloydb-auth-proxy \ + --auto-iam-authn \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE' + + In addition, Auto IAM AuthN may be enabled on a per-instance basis with the + query string syntax described above. + + ./alloydb-auth-proxy \ + 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE?auto-iam-authn=true' Health checks @@ -436,11 +476,13 @@ status code.`) // Global and per instance flags pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1", - "Address on which to bind AlloyDB instance listeners.") + "(*) Address on which to bind AlloyDB instance listeners.") pflags.IntVarP(&c.conf.Port, "port", "p", 5432, - "Initial port to use for listeners. Subsequent listeners increment from this value.") + "(*) Initial port to use for listeners. Subsequent listeners increment from this value.") pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "", - `Enables Unix sockets for all listeners using the provided directory.`) + `(*) Enables Unix sockets for all listeners using the provided directory.`) + pflags.BoolVarP(&c.conf.AutoIAMAuthN, "auto-iam-authn", "i", false, + "(*) Enables Automatic IAM Authentication for all instances") v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_"))) v.SetEnvPrefix(envPrefix) @@ -629,6 +671,11 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { ic.UnixSocket = u[0] } + + ic.AutoIAMAuthN, err = parseBoolOpt(q, "auto-iam-authn") + if err != nil { + return err + } } ics = append(ics, ic) } @@ -637,6 +684,37 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error { return nil } +// parseBoolOpt parses a boolean option from the query string. +// True is can be "t", "true" (case-insensitive). +// False can be "f" or "false" (case-insensitive). +func parseBoolOpt(q url.Values, name string) (bool, error) { + v, ok := q[name] + if !ok { + return false, nil + } + + if len(v) != 1 { + return false, newBadCommandError( + fmt.Sprintf("%v param should be only one value: %q", name, v), + ) + } + + switch strings.ToLower(v[0]) { + // if only the key is present (and the value is empty string), accept that + // as true. + case "true", "t", "": + return true, nil + case "false", "f": + return false, nil + default: + // value is not recognized + return false, newBadCommandError( + fmt.Sprintf("%v query param should be true or false, got: %q", + name, v[0], + )) + } +} + // runSignalWrapper watches for SIGTERM and SIGINT and interupts execution if necessary. func runSignalWrapper(cmd *Command) (err error) { defer cmd.cleanup() diff --git a/cmd/root_test.go b/cmd/root_test.go index a45bcc50..2f06fe53 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -142,6 +142,61 @@ func TestNewCommandArguments(t *testing.T) { Instances: []proxy.InstanceConnConfig{{Name: "projects/proj/locations/region/clusters/clust/instances/inst"}}, }), }, + { + desc: "Auto IAM AuthN", + args: []string{ + "--auto-iam-authn", + "projects/proj/locations/region/clusters/clust/instances/inst", + }, + want: withDefaults(&proxy.Config{ + AutoIAMAuthN: true, + Instances: []proxy.InstanceConnConfig{{Name: "projects/proj/locations/region/clusters/clust/instances/inst"}}, + }), + }, + { + desc: "Auto IAM AuthN query param (key only)", + args: []string{ + "projects/proj/locations/region/clusters/clust/instances/inst?auto-iam-authn", + }, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + AutoIAMAuthN: true, + Name: "projects/proj/locations/region/clusters/clust/instances/inst", + }}, + }), + }, + { + desc: "Auto IAM AuthN query param (t & f)", + args: []string{ + "projects/proj/locations/region/clusters/clust/instances/inst1?auto-iam-authn=t", + "projects/proj/locations/region/clusters/clust/instances/inst2?auto-iam-authn=f", + }, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + AutoIAMAuthN: true, + Name: "projects/proj/locations/region/clusters/clust/instances/inst1", + }, { + AutoIAMAuthN: false, + Name: "projects/proj/locations/region/clusters/clust/instances/inst2", + }}, + }), + }, + { + desc: "Auto IAM AuthN query param (true & false)", + args: []string{ + "projects/proj/locations/region/clusters/clust/instances/inst1?auto-iam-authn=true", + "projects/proj/locations/region/clusters/clust/instances/inst2?auto-iam-authn=false", + }, + want: withDefaults(&proxy.Config{ + Instances: []proxy.InstanceConnConfig{{ + AutoIAMAuthN: true, + Name: "projects/proj/locations/region/clusters/clust/instances/inst1", + }, { + AutoIAMAuthN: false, + Name: "projects/proj/locations/region/clusters/clust/instances/inst2", + }}, + }), + }, { desc: "using the address flag", args: []string{"--address", "0.0.0.0", "projects/proj/locations/region/clusters/clust/instances/inst"}, diff --git a/go.mod b/go.mod index 3ffa7bf9..4305338c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/GoogleCloudPlatform/alloydb-auth-proxy go 1.20 require ( - cloud.google.com/go/alloydbconn v1.4.1 + cloud.google.com/go/alloydbconn v1.5.0 contrib.go.opencensus.io/exporter/prometheus v0.4.2 contrib.go.opencensus.io/exporter/stackdriver v0.13.14 github.com/coreos/go-systemd/v22 v22.5.0 @@ -22,13 +22,13 @@ require ( ) require ( - cloud.google.com/go v0.110.8 // indirect - cloud.google.com/go/alloydb v1.6.0 // indirect - cloud.google.com/go/compute v1.23.1 // indirect + cloud.google.com/go v0.110.9 // indirect + cloud.google.com/go/alloydb v1.7.0 // indirect + cloud.google.com/go/compute v1.23.2 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/longrunning v0.5.2 // indirect - cloud.google.com/go/monitoring v1.16.1 // indirect - cloud.google.com/go/trace v1.10.2 // indirect + cloud.google.com/go/longrunning v0.5.3 // indirect + cloud.google.com/go/monitoring v1.16.2 // indirect + cloud.google.com/go/trace v1.10.3 // indirect github.com/aws/aws-sdk-go v1.43.31 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect @@ -70,11 +70,11 @@ require ( golang.org/x/net v0.18.0 // indirect golang.org/x/sync v0.5.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect + google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 3cae44b2..b0d28af3 100644 --- a/go.sum +++ b/go.sum @@ -31,12 +31,12 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go/alloydb v1.6.0 h1:cLXEzXA3wZNJLY5OJPmUdJbgXMiZqLTHIwJwOZ52Ggs= -cloud.google.com/go/alloydb v1.6.0/go.mod h1:/hPjP/H+sU/PhA2Ifpbd80POfQCfwtCWacxbOf6zCkI= -cloud.google.com/go/alloydbconn v1.4.1 h1:ElQKVweBZJs+vgiYwsrvAzZ4pTu4nPyc/ZjqvrPfjDk= -cloud.google.com/go/alloydbconn v1.4.1/go.mod h1:LE3fuyXbj2RWWkm3iNdJr+LO5ppkMgCmNXxnav1aYmA= +cloud.google.com/go v0.110.9 h1:e7ITSqGFFk4rbz/JFIqZh3G4VEHguhAL4BQcFlWtU68= +cloud.google.com/go v0.110.9/go.mod h1:rpxevX/0Lqvlbc88b7Sc1SPNdyK1riNBTUU6JXhYNpM= +cloud.google.com/go/alloydb v1.7.0 h1:4En+yNZ5yGrRFgT/CJqwFQ/LH+wZAWoOwud5Tln2ggg= +cloud.google.com/go/alloydb v1.7.0/go.mod h1:3cVvH8uiM4VrVTKMq+hsJ8YY5RiQfXxj6gEgc8bFIgg= +cloud.google.com/go/alloydbconn v1.5.0 h1:PSNAKxdbpkji8ZBJRyXoP89KuWbd/jbAgysaSzvVNBk= +cloud.google.com/go/alloydbconn v1.5.0/go.mod h1:8vInty4jdOKRhtofZl5lB1IDKY362cbdik4bCGezTP4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -46,17 +46,17 @@ cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM7 cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.23.1 h1:V97tBoDaZHb6leicZ1G6DLK2BAaZLJ/7+9BB/En3hR0= -cloud.google.com/go/compute v1.23.1/go.mod h1:CqB3xpmPKKt3OJpW2ndFIXnA9A4xAy/F3Xp1ixncW78= +cloud.google.com/go/compute v1.23.2 h1:nWEMDhgbBkBJjfpVySqU4jgWdc22PLR0o4vEexZHers= +cloud.google.com/go/compute v1.23.2/go.mod h1:JJ0atRC0J/oWYiiVBmsSsrRnh92DhZPG4hFDcR04Rns= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/longrunning v0.5.2 h1:u+oFqfEwwU7F9dIELigxbe0XVnBAo9wqMuQLA50CZ5k= -cloud.google.com/go/longrunning v0.5.2/go.mod h1:nqo6DQbNV2pXhGDbDMoN2bWz68MjZUzqv2YttZiveCs= -cloud.google.com/go/monitoring v1.16.1 h1:CTklIuUkS5nCricGojPwdkSgPsCTX2HmYTxFDg+UvpU= -cloud.google.com/go/monitoring v1.16.1/go.mod h1:6HsxddR+3y9j+o/cMJH6q/KJ/CBTvM/38L/1m7bTRJ4= +cloud.google.com/go/longrunning v0.5.3 h1:maKa7O9YTzmVzwdlRKr981U1Ys2auup6rpeMt8y3+RU= +cloud.google.com/go/longrunning v0.5.3/go.mod h1:y/0ga59EYu58J6SHmmQOvekvND2qODbu8ywBBW7EK7Y= +cloud.google.com/go/monitoring v1.16.2 h1:gx7BDZcoRqX5DfuJzw9LdhVjEkqCLmDXScdnrmIy9ik= +cloud.google.com/go/monitoring v1.16.2/go.mod h1:B44KGwi4ZCF8Rk/5n+FWeispDXoKSk9oss2QNlXJBgc= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -67,8 +67,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/trace v1.10.2 h1:80Rh4JSqJLfe/xGNrpyO4MQxiFDXcHG1XrsevfmrIRQ= -cloud.google.com/go/trace v1.10.2/go.mod h1:NPXemMi6MToRFcSxRl2uDnu/qAlAQ3oULUphcHGh1vA= +cloud.google.com/go/trace v1.10.3 h1:yiQ2EdegdiZDPwNPoUqGYAMd0wNValT/NRsC1XGyBJ0= +cloud.google.com/go/trace v1.10.3/go.mod h1:Ke1bgfc73RV3wUFml+uQp7EsDw4dGaETLxB7Iq/r4CY= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4= @@ -1523,8 +1523,8 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= +golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1726,12 +1726,12 @@ google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4= google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index f2d5cded..82c0bf31 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -56,6 +56,10 @@ type InstanceConnConfig struct { // necessary. If set, UnixSocketPath takes precedence over UnixSocket, Addr // and Port. UnixSocketPath string + + // AutoIAMAuthN enables automatic IAM authentication on the instance only. + // See Config.AutoIAMAuthN for more details. + AutoIAMAuthN bool } // Config contains all the configuration provided by the caller. @@ -64,6 +68,11 @@ type Config struct { // API. UserAgent string + // AutoIAMAuthN enabled automatic IAM authentication which results in the + // Proxy sending the IAM principal's OAuth2 token to the backend to enable + // a passwordless login for callers. + AutoIAMAuthN bool + // Token is the Bearer token used for authorization. Token string @@ -272,6 +281,10 @@ func (c *Config) DialerOptions(l alloydb.Logger) ([]alloydbconn.Option, error) { opts = append(opts, alloydbconn.WithAdminAPIEndpoint(c.APIEndpointURL)) } + if c.AutoIAMAuthN { + opts = append(opts, alloydbconn.WithIAMAuthN()) + } + return opts, nil } diff --git a/tests/alloydb_test.go b/tests/alloydb_test.go index a542485a..949cf733 100644 --- a/tests/alloydb_test.go +++ b/tests/alloydb_test.go @@ -36,6 +36,11 @@ projects//locations//clusters//instances/`, os.Getenv("ALLOYDB_USER"), "Name of database user.", ) + alloydbIAMUser = flag.String( + "alloydb_iam_user", + os.Getenv("ALLOYDB_IAM_USER"), + "Name of database user.", + ) alloydbPass = flag.String( "alloydb_pass", os.Getenv("ALLOYDB_PASS"), @@ -54,6 +59,8 @@ func requirePostgresVars(t *testing.T) { t.Fatal("'alloydb_conn_name' not set") case *alloydbUser: t.Fatal("'alloydb_user' not set") + case *alloydbIAMUser: + t.Fatal("'alloydb_iam_user' not set") case *alloydbPass: t.Fatal("'alloydb_pass' not set") case *alloydbDB: @@ -67,11 +74,24 @@ func TestPostgresTCP(t *testing.T) { } requirePostgresVars(t) - dsn := fmt.Sprintf("host=127.0.0.1 user=%v password=%v database=%v sslmode=disable", - *alloydbUser, *alloydbPass, *alloydbDB) + dsn := fmt.Sprintf( + "host=127.0.0.1 user=%v password=%v database=%v sslmode=disable", + *alloydbUser, *alloydbPass, *alloydbDB, + ) proxyConnTest(t, []string{*alloydbInstanceName}, "pgx", dsn) } +func TestPostgresAutoIAMAuthN(t *testing.T) { + if testing.Short() { + t.Skip("skipping Postgres integration tests") + } + requirePostgresVars(t) + + dsn := fmt.Sprintf("host=127.0.0.1 user=%v database=%v sslmode=disable", + *alloydbIAMUser, *alloydbDB) + proxyConnTest(t, []string{*alloydbInstanceName, "--auto-iam-authn"}, "pgx", dsn) +} + func createTempDir(t *testing.T) (string, func()) { testDir, err := os.MkdirTemp("", "*") if err != nil {