Skip to content

Commit

Permalink
GODRIVER-2806: Implement automatic GCP token acquisition (#1708)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Dale <[email protected]>
  • Loading branch information
pmeredit and matthewdale authored Jul 24, 2024
1 parent 570ae17 commit 7c5046d
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 5 deletions.
51 changes: 51 additions & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2000,6 +2000,31 @@ tasks:
export AZUREOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=azure OIDC=oidc ./etc/run-oidc-test.sh ./test"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/azure/run-driver-test.sh
- name: "oidc-auth-test-gcp-latest"
commands:
- command: shell.exec
params:
working_dir: src/go.mongodb.org/mongo-driver
shell: bash
script: |-
set -o errexit
${PREPARE_SHELL}
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
# we need to statically link libc to avoid the situation where the VM has a different
# version of libc
go build -tags osusergo,netgo -ldflags '-w -extldflags "-static -lgcc -lc"' -o test ./cmd/testoidcauth/main.go
rm "$GCPOIDC_DRIVERS_TAR_FILE" || true
tar -cf $GCPOIDC_DRIVERS_TAR_FILE ./test
tar -uf $GCPOIDC_DRIVERS_TAR_FILE ./etc
rm "$GCPOIDC_DRIVERS_TAR_FILE".gz || true
gzip $GCPOIDC_DRIVERS_TAR_FILE
export GCPOIDC_DRIVERS_TAR_FILE=/tmp/mongo-go-driver.tar.gz
# Define the command to run on the gcp VM.
# Ensure that we source the environment file created for us, set up any other variables we need,
# and then run our test suite on the vm.
export GCPOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=gcp OIDC=oidc ./etc/run-oidc-test.sh ./test"
bash $DRIVERS_TOOLS/.evergreen/auth_oidc/gcp/run-driver-test.sh
- name: "test-search-index"
commands:
- func: "bootstrap-mongo-orchestration"
Expand Down Expand Up @@ -2342,6 +2367,30 @@ task_groups:
tasks:
- oidc-auth-test-azure-latest

- name: testgcpoidc_task_group
setup_group:
- func: fetch-source
- func: prepare-resources
- func: fix-absolute-paths
- func: make-files-executable
- command: subprocess.exec
params:
binary: bash
env:
AZUREOIDC_VMNAME_PREFIX: "GO_DRIVER"
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/setup.sh
teardown_task:
- command: subprocess.exec
params:
binary: bash
args:
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
setup_group_can_fail_task: true
setup_group_timeout_secs: 1800
tasks:
- oidc-auth-test-gcp-latest

- name: test-aws-lambda-task-group
setup_group:
- func: fetch-source
Expand Down Expand Up @@ -2693,3 +2742,5 @@ buildvariants:
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
- name: testazureoidc_task_group
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
- name: testgcpoidc_task_group
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
22 changes: 22 additions & 0 deletions cmd/testoidcauth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ func main() {
case "azure":
aux("machine_5_1_azureWithNoUsername", machine51azureWithNoUsername)
aux("machine_5_2_azureWithNoUsername", machine52azureWithBadUsername)
case "gcp":
aux("machine_6_1_gcpWithNoUsername", machine61gcpWithNoUsername)
default:
log.Fatal("Unknown OIDC_ENV: ", env)
}
Expand Down Expand Up @@ -736,3 +738,23 @@ func machine52azureWithBadUsername() error {
}
return nil
}

func machine61gcpWithNoUsername() error {
opts := options.Client().ApplyURI(uriSingle)
if opts == nil || opts.Auth == nil {
return fmt.Errorf("machine_6_1: failed parsing uri: %q", uriSingle)
}
client, err := mongo.Connect(context.Background(), opts)
if err != nil {
return fmt.Errorf("machine_6_1: failed connecting client: %v", err)
}
defer client.Disconnect(context.Background())

coll := client.Database("test").Collection("test")

_, err = coll.Find(context.Background(), bson.D{})
if err != nil {
return fmt.Errorf("machine_6_1: failed executing Find: %v", err)
}
return nil
}
42 changes: 37 additions & 5 deletions x/mongo/driver/auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
Expand Down Expand Up @@ -174,12 +175,12 @@ func (oa *OIDCAuthenticator) providerCallback() (OIDCCallback, error) {
return nil, newAuthError(fmt.Sprintf("%q must be specified for Azure OIDC", resourceProp), nil)
}
return getAzureOIDCCallback(oa.userName, resource, oa.httpClient), nil
// TODO GODRIVER-2806: Automatic token acquisition for GCP Identity Provider
// This is here just to pass the linter, it will be fixed in one of the above tickets.
case gcpEnvironmentValue:
return func(ctx context.Context, args *OIDCArgs) (*OIDCCredential, error) {
return nil, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
}, fmt.Errorf("automatic token acquisition for %q not implemented yet", env)
resource, ok := oa.AuthMechanismProperties[resourceProp]
if !ok {
return nil, newAuthError(fmt.Sprintf("%q must be specified for GCP OIDC", resourceProp), nil)
}
return getGCPOIDCCallback(resource, oa.httpClient), nil
}

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

// getGCPOIDCCallback returns the callback for the GCP Identity Provider.
func getGCPOIDCCallback(resource string, httpClient *http.Client) OIDCCallback {
// return the callback parameterized by the clientID and resource, also passing in the user
// configured httpClient.
return func(ctx context.Context, args *OIDCArgs) (*OIDCCredential, error) {
resource = url.QueryEscape(resource)
uri := fmt.Sprintf("http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=%s", resource)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, newAuthError("error creating http request to GCP Identity Provider", err)
}
req.Header.Add("Metadata-Flavor", "Google")
resp, err := httpClient.Do(req)
if err != nil {
return nil, newAuthError("error getting access token from GCP Identity Provider", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, newAuthError(fmt.Sprintf("failed to get a valid response from GCP Identity Provider, http code: %d", resp.StatusCode), nil)
}
accessToken, err := io.ReadAll(resp.Body)
if err != nil {
return nil, newAuthError("failed parsing reading response from GCP Identity Provider", err)
}
return &OIDCCredential{
AccessToken: string(accessToken),
ExpiresAt: nil,
}, nil
}
}

func (oa *OIDCAuthenticator) getAccessToken(
ctx context.Context,
conn driver.Connection,
Expand Down

0 comments on commit 7c5046d

Please sign in to comment.