Skip to content

Commit 0ed76dc

Browse files
pmereditblink1073
authored andcommitted
GODRIVER-2806: Implement automatic GCP token acquisition (mongodb#1708)
Co-authored-by: Matt Dale <[email protected]> (cherry picked from commit 7c5046d)
1 parent f152d38 commit 0ed76dc

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

.evergreen/config.yml

+51
Original file line numberDiff line numberDiff line change
@@ -1995,6 +1995,31 @@ tasks:
19951995
export AZUREOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=azure OIDC=oidc ./etc/run-oidc-test.sh ./test"
19961996
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
19971997
1998+
- name: "oidc-auth-test-gcp-latest"
1999+
commands:
2000+
- command: shell.exec
2001+
params:
2002+
working_dir: src/go.mongodb.org/mongo-driver
2003+
shell: bash
2004+
script: |-
2005+
set -o errexit
2006+
${PREPARE_SHELL}
2007+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
2008+
# we need to statically link libc to avoid the situation where the VM has a different
2009+
# version of libc
2010+
go build -tags osusergo,netgo -ldflags '-w -extldflags "-static -lgcc -lc"' -o test ./cmd/testoidcauth/main.go
2011+
rm "$GCPOIDC_DRIVERS_TAR_FILE" || true
2012+
tar -cf $GCPOIDC_DRIVERS_TAR_FILE ./test
2013+
tar -uf $GCPOIDC_DRIVERS_TAR_FILE ./etc
2014+
rm "$GCPOIDC_DRIVERS_TAR_FILE".gz || true
2015+
gzip $GCPOIDC_DRIVERS_TAR_FILE
2016+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
2017+
# Define the command to run on the gcp VM.
2018+
# Ensure that we source the environment file created for us, set up any other variables we need,
2019+
# and then run our test suite on the vm.
2020+
export GCPOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=gcp OIDC=oidc ./etc/run-oidc-test.sh ./test"
2021+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
2022+
19982023
- name: "test-search-index"
19992024
commands:
20002025
- func: "bootstrap-mongo-orchestration"
@@ -2326,6 +2351,30 @@ task_groups:
23262351
tasks:
23272352
- oidc-auth-test-azure-latest
23282353

2354+
- name: testgcpoidc_task_group
2355+
setup_group:
2356+
- func: fetch-source
2357+
- func: prepare-resources
2358+
- func: fix-absolute-paths
2359+
- func: make-files-executable
2360+
- command: subprocess.exec
2361+
params:
2362+
binary: bash
2363+
env:
2364+
AZUREOIDC_VMNAME_PREFIX: "GO_DRIVER"
2365+
args:
2366+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
2367+
teardown_task:
2368+
- command: subprocess.exec
2369+
params:
2370+
binary: bash
2371+
args:
2372+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
2373+
setup_group_can_fail_task: true
2374+
setup_group_timeout_secs: 1800
2375+
tasks:
2376+
- oidc-auth-test-gcp-latest
2377+
23292378
- name: test-aws-lambda-task-group
23302379
setup_group:
23312380
- func: fetch-source
@@ -2671,3 +2720,5 @@ buildvariants:
26712720
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
26722721
- name: testazureoidc_task_group
26732722
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
2723+
- name: testgcpoidc_task_group
2724+
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README

internal/cmd/testoidcauth/main.go

+22
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ func main() {
9595
case "azure":
9696
aux("machine_5_1_azureWithNoUsername", machine51azureWithNoUsername)
9797
aux("machine_5_2_azureWithNoUsername", machine52azureWithBadUsername)
98+
case "gcp":
99+
aux("machine_6_1_gcpWithNoUsername", machine61gcpWithNoUsername)
98100
default:
99101
log.Fatal("Unknown OIDC_ENV: ", env)
100102
}
@@ -745,3 +747,23 @@ func machine52azureWithBadUsername() error {
745747
}
746748
return nil
747749
}
750+
751+
func machine61gcpWithNoUsername() error {
752+
opts := options.Client().ApplyURI(uriSingle)
753+
if opts == nil || opts.Auth == nil {
754+
return fmt.Errorf("machine_6_1: failed parsing uri: %q", uriSingle)
755+
}
756+
client, err := mongo.Connect(context.Background(), opts)
757+
if err != nil {
758+
return fmt.Errorf("machine_6_1: failed connecting client: %v", err)
759+
}
760+
defer client.Disconnect(context.Background())
761+
762+
coll := client.Database("test").Collection("test")
763+
764+
_, err = coll.Find(context.Background(), bson.D{})
765+
if err != nil {
766+
return fmt.Errorf("machine_6_1: failed executing Find: %v", err)
767+
}
768+
return nil
769+
}

x/mongo/driver/auth/oidc.go

+37-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"encoding/json"
1212
"fmt"
13+
"io"
1314
"net/http"
1415
"net/url"
1516
"strings"
@@ -175,12 +176,12 @@ func (oa *OIDCAuthenticator) providerCallback() (OIDCCallback, error) {
175176
return nil, newAuthError(fmt.Sprintf("%q must be specified for Azure OIDC", resourceProp), nil)
176177
}
177178
return getAzureOIDCCallback(oa.userName, resource, oa.httpClient), nil
178-
// TODO GODRIVER-2806: Automatic token acquisition for GCP Identity Provider
179-
// This is here just to pass the linter, it will be fixed in one of the above tickets.
180179
case gcpEnvironmentValue:
181-
return func(ctx context.Context, args *OIDCArgs) (*OIDCCredential, error) {
182-
return nil, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
183-
}, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
180+
resource, ok := oa.AuthMechanismProperties[resourceProp]
181+
if !ok {
182+
return nil, newAuthError(fmt.Sprintf("%q must be specified for GCP OIDC", resourceProp), nil)
183+
}
184+
return getGCPOIDCCallback(resource, oa.httpClient), nil
184185
}
185186

186187
return nil, fmt.Errorf("%q %q not supported for MONGODB-OIDC", environmentProp, env)
@@ -229,6 +230,37 @@ func getAzureOIDCCallback(clientID string, resource string, httpClient *http.Cli
229230
}
230231
}
231232

233+
// getGCPOIDCCallback returns the callback for the GCP Identity Provider.
234+
func getGCPOIDCCallback(resource string, httpClient *http.Client) OIDCCallback {
235+
// return the callback parameterized by the clientID and resource, also passing in the user
236+
// configured httpClient.
237+
return func(ctx context.Context, args *OIDCArgs) (*OIDCCredential, error) {
238+
resource = url.QueryEscape(resource)
239+
uri := fmt.Sprintf("http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=%s", resource)
240+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
241+
if err != nil {
242+
return nil, newAuthError("error creating http request to GCP Identity Provider", err)
243+
}
244+
req.Header.Add("Metadata-Flavor", "Google")
245+
resp, err := httpClient.Do(req)
246+
if err != nil {
247+
return nil, newAuthError("error getting access token from GCP Identity Provider", err)
248+
}
249+
defer resp.Body.Close()
250+
if resp.StatusCode != http.StatusOK {
251+
return nil, newAuthError(fmt.Sprintf("failed to get a valid response from GCP Identity Provider, http code: %d", resp.StatusCode), nil)
252+
}
253+
accessToken, err := io.ReadAll(resp.Body)
254+
if err != nil {
255+
return nil, newAuthError("failed parsing reading response from GCP Identity Provider", err)
256+
}
257+
return &OIDCCredential{
258+
AccessToken: string(accessToken),
259+
ExpiresAt: nil,
260+
}, nil
261+
}
262+
}
263+
232264
func (oa *OIDCAuthenticator) getAccessToken(
233265
ctx context.Context,
234266
conn *mnet.Connection,

0 commit comments

Comments
 (0)