Skip to content

Commit 7c5046d

Browse files
GODRIVER-2806: Implement automatic GCP token acquisition (mongodb#1708)
Co-authored-by: Matt Dale <[email protected]>
1 parent 570ae17 commit 7c5046d

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
@@ -2000,6 +2000,31 @@ tasks:
20002000
export AZUREOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=azure OIDC=oidc ./etc/run-oidc-test.sh ./test"
20012001
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
20022002
2003+
- name: "oidc-auth-test-gcp-latest"
2004+
commands:
2005+
- command: shell.exec
2006+
params:
2007+
working_dir: src/go.mongodb.org/mongo-driver
2008+
shell: bash
2009+
script: |-
2010+
set -o errexit
2011+
${PREPARE_SHELL}
2012+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
2013+
# we need to statically link libc to avoid the situation where the VM has a different
2014+
# version of libc
2015+
go build -tags osusergo,netgo -ldflags '-w -extldflags "-static -lgcc -lc"' -o test ./cmd/testoidcauth/main.go
2016+
rm "$GCPOIDC_DRIVERS_TAR_FILE" || true
2017+
tar -cf $GCPOIDC_DRIVERS_TAR_FILE ./test
2018+
tar -uf $GCPOIDC_DRIVERS_TAR_FILE ./etc
2019+
rm "$GCPOIDC_DRIVERS_TAR_FILE".gz || true
2020+
gzip $GCPOIDC_DRIVERS_TAR_FILE
2021+
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
2022+
# Define the command to run on the gcp VM.
2023+
# Ensure that we source the environment file created for us, set up any other variables we need,
2024+
# and then run our test suite on the vm.
2025+
export GCPOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=gcp OIDC=oidc ./etc/run-oidc-test.sh ./test"
2026+
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
2027+
20032028
- name: "test-search-index"
20042029
commands:
20052030
- func: "bootstrap-mongo-orchestration"
@@ -2342,6 +2367,30 @@ task_groups:
23422367
tasks:
23432368
- oidc-auth-test-azure-latest
23442369

2370+
- name: testgcpoidc_task_group
2371+
setup_group:
2372+
- func: fetch-source
2373+
- func: prepare-resources
2374+
- func: fix-absolute-paths
2375+
- func: make-files-executable
2376+
- command: subprocess.exec
2377+
params:
2378+
binary: bash
2379+
env:
2380+
AZUREOIDC_VMNAME_PREFIX: "GO_DRIVER"
2381+
args:
2382+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
2383+
teardown_task:
2384+
- command: subprocess.exec
2385+
params:
2386+
binary: bash
2387+
args:
2388+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
2389+
setup_group_can_fail_task: true
2390+
setup_group_timeout_secs: 1800
2391+
tasks:
2392+
- oidc-auth-test-gcp-latest
2393+
23452394
- name: test-aws-lambda-task-group
23462395
setup_group:
23472396
- func: fetch-source
@@ -2693,3 +2742,5 @@ buildvariants:
26932742
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
26942743
- name: testazureoidc_task_group
26952744
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
2745+
- name: testgcpoidc_task_group
2746+
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README

cmd/testoidcauth/main.go

+22
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ func main() {
9191
case "azure":
9292
aux("machine_5_1_azureWithNoUsername", machine51azureWithNoUsername)
9393
aux("machine_5_2_azureWithNoUsername", machine52azureWithBadUsername)
94+
case "gcp":
95+
aux("machine_6_1_gcpWithNoUsername", machine61gcpWithNoUsername)
9496
default:
9597
log.Fatal("Unknown OIDC_ENV: ", env)
9698
}
@@ -736,3 +738,23 @@ func machine52azureWithBadUsername() error {
736738
}
737739
return nil
738740
}
741+
742+
func machine61gcpWithNoUsername() error {
743+
opts := options.Client().ApplyURI(uriSingle)
744+
if opts == nil || opts.Auth == nil {
745+
return fmt.Errorf("machine_6_1: failed parsing uri: %q", uriSingle)
746+
}
747+
client, err := mongo.Connect(context.Background(), opts)
748+
if err != nil {
749+
return fmt.Errorf("machine_6_1: failed connecting client: %v", err)
750+
}
751+
defer client.Disconnect(context.Background())
752+
753+
coll := client.Database("test").Collection("test")
754+
755+
_, err = coll.Find(context.Background(), bson.D{})
756+
if err != nil {
757+
return fmt.Errorf("machine_6_1: failed executing Find: %v", err)
758+
}
759+
return nil
760+
}

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"
@@ -174,12 +175,12 @@ func (oa *OIDCAuthenticator) providerCallback() (OIDCCallback, error) {
174175
return nil, newAuthError(fmt.Sprintf("%q must be specified for Azure OIDC", resourceProp), nil)
175176
}
176177
return getAzureOIDCCallback(oa.userName, resource, oa.httpClient), nil
177-
// TODO GODRIVER-2806: Automatic token acquisition for GCP Identity Provider
178-
// This is here just to pass the linter, it will be fixed in one of the above tickets.
179178
case gcpEnvironmentValue:
180-
return func(ctx context.Context, args *OIDCArgs) (*OIDCCredential, error) {
181-
return nil, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
182-
}, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
179+
resource, ok := oa.AuthMechanismProperties[resourceProp]
180+
if !ok {
181+
return nil, newAuthError(fmt.Sprintf("%q must be specified for GCP OIDC", resourceProp), nil)
182+
}
183+
return getGCPOIDCCallback(resource, oa.httpClient), nil
183184
}
184185

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

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

0 commit comments

Comments
 (0)