Skip to content

Commit 63b1504

Browse files
committed
GODRIVER-3175 Add Kubernetes support for OIDC.
1 parent ee212da commit 63b1504

File tree

9 files changed

+886
-549
lines changed

9 files changed

+886
-549
lines changed

.evergreen/config.yml

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,18 @@ functions:
162162
args:
163163
- ${DRIVERS_TOOLS}/.evergreen/teardown.sh
164164

165-
assume-ec2-role:
165+
assume-test-secrets-ec2-role:
166166
- command: ec2.assume_role
167167
params:
168168
role_arn: ${aws_test_secrets_role}
169+
duration_seconds: 1800
169170

170171
run-oidc-auth-test-with-test-credentials:
171172
- command: subprocess.exec
172173
type: test
173174
params:
174175
binary: bash
175-
env:
176+
env:
176177
OIDC: oidc
177178
include_expansions_in_env: [DRIVERS_TOOLS, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN]
178179
args: [*task-runner, test-oidc]
@@ -396,6 +397,16 @@ functions:
396397
include_expansions_in_env: ["MONGODB_URI"]
397398
args: [*task-runner, test-goleak]
398399

400+
"run oidc k8s test":
401+
- command: subprocess.exec
402+
type: test
403+
params:
404+
binary: bash
405+
include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, VARIANT, DRIVERS_TOOLS]
406+
env:
407+
OIDC_ENV: k8s
408+
args: [*task-runner, test-oidc-remote]
409+
399410
run-ocsp-server:
400411
- command: subprocess.exec
401412
params:
@@ -1658,6 +1669,19 @@ tasks:
16581669
OIDC_ENV: gcp
16591670
args: [*task-runner, test-oidc-remote]
16601671

1672+
- name: "oidc-auth-test-k8s"
1673+
commands:
1674+
- func: assume-test-secrets-ec2-role
1675+
- func: "run oidc k8s test"
1676+
vars:
1677+
VARIANT: eks
1678+
- func: "run oidc k8s test"
1679+
vars:
1680+
VARIANT: gke
1681+
- func: "run oidc k8s test"
1682+
vars:
1683+
VARIANT: aks
1684+
16611685
- name: "test-search-index"
16621686
commands:
16631687
- func: "bootstrap-mongo-orchestration"
@@ -1902,14 +1926,17 @@ task_groups:
19021926
- ${DRIVERS_TOOLS}/.evergreen/csfle/azurekms/teardown.sh
19031927
- func: teardown
19041928
- func: handle-test-artifacts
1905-
19061929
tasks:
19071930
- testazurekms-task
19081931

19091932
- name: testoidc_task_group
1933+
setup_group_can_fail_task: true
1934+
setup_group_timeout_secs: 1800
1935+
teardown_task_can_fail_task: true
1936+
teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)
19101937
setup_group:
19111938
- func: setup-system
1912-
- func: assume-ec2-role
1939+
- func: assume-test-secrets-ec2-role
19131940
- command: subprocess.exec
19141941
params:
19151942
binary: bash
@@ -1926,12 +1953,14 @@ task_groups:
19261953
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/teardown.sh
19271954
- func: teardown
19281955
- func: handle-test-artifacts
1929-
setup_group_can_fail_task: true
1930-
setup_group_timeout_secs: 1800
19311956
tasks:
19321957
- oidc-auth-test
19331958

19341959
- name: testazureoidc_task_group
1960+
setup_group_can_fail_task: true
1961+
setup_group_timeout_secs: 1800
1962+
teardown_task_can_fail_task: true
1963+
teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)
19351964
setup_group:
19361965
- func: setup-system
19371966
- command: subprocess.exec
@@ -1949,12 +1978,14 @@ task_groups:
19491978
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/azure/teardown.sh
19501979
- func: teardown
19511980
- func: handle-test-artifacts
1952-
setup_group_can_fail_task: true
1953-
setup_group_timeout_secs: 1800
19541981
tasks:
19551982
- oidc-auth-test-azure
19561983

19571984
- name: testgcpoidc_task_group
1985+
setup_group_can_fail_task: true
1986+
setup_group_timeout_secs: 1800
1987+
teardown_task_can_fail_task: true
1988+
teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)
19581989
setup_group:
19591990
- func: setup-system
19601991
- command: subprocess.exec
@@ -1972,10 +2003,32 @@ task_groups:
19722003
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/teardown.sh
19732004
- func: teardown
19742005
- func: handle-test-artifacts
2006+
tasks:
2007+
- oidc-auth-test-gcp
2008+
2009+
- name: testk8soidc_task_group
19752010
setup_group_can_fail_task: true
19762011
setup_group_timeout_secs: 1800
2012+
teardown_task_can_fail_task: true
2013+
teardown_group_timeout_secs: 180 # 3 minutes (max allowed time)
2014+
setup_group:
2015+
- func: setup-system
2016+
- command: subprocess.exec
2017+
params:
2018+
binary: bash
2019+
include_expansions_in_env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN"]
2020+
args:
2021+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup.sh
2022+
teardown_group:
2023+
- command: subprocess.exec
2024+
params:
2025+
binary: bash
2026+
args:
2027+
- ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown.sh
2028+
- func: teardown
2029+
- func: handle-test-artifacts
19772030
tasks:
1978-
- oidc-auth-test-gcp
2031+
- oidc-auth-test-k8s
19792032

19802033
- name: test-aws-lambda-task-group
19812034
setup_group:
@@ -2322,8 +2375,6 @@ buildvariants:
23222375
GO_DIST: "/opt/golang/go1.22"
23232376
tasks:
23242377
- name: testoidc_task_group
2325-
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
23262378
- name: testazureoidc_task_group
2327-
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
23282379
- name: testgcpoidc_task_group
2329-
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
2380+
- name: testk8soidc_task_group

etc/run-oidc-remote-test.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ elif [ $OIDC_ENV == "gcp" ]; then
3131
export GCPOIDC_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=gcp OIDC=oidc ./etc/run-oidc-test.sh ./test"
3232
bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/gcp/run-driver-test.sh
3333

34+
elif [ $OIDC_ENV == "k8s" ]; then
35+
export K8S_VARIANT=${VARIANT}
36+
export K8S_DRIVERS_TAR_FILE=$DRIVERS_TAR_FILE
37+
export K8S_TEST_CMD="PROJECT_DIRECTORY='.' OIDC_ENV=k8s OIDC=oidc ./etc/run-oidc-test.sh ./test"
38+
bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/setup-pod.sh
39+
bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/run-driver-test.sh
40+
bash ${DRIVERS_TOOLS}/.evergreen/auth_oidc/k8s/teardown-pod.sh
41+
3442
else
3543
echo "Unrecognized OIDC_ENV $OIDC_ENV"
3644
exit 1

etc/run-oidc-test.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ elif [ $OIDC_ENV == "azure" ]; then
2121
elif [ $OIDC_ENV == "gcp" ]; then
2222
source ./secrets-export.sh
2323

24+
elif [ $OIDC_ENV == "k8s" ]; then
25+
# "run-driver-test.sh" in drivers-evergreen-tools takes care of sourcing
26+
# "secrets-export.sh". Nothing to do in this block, but we still need a
27+
# command to be syntactically valid, so use no-op command ":".
28+
:
29+
2430
else
2531
echo "Unrecognized OIDC_ENV $OIDC_ENV"
2632
exit 1

internal/cmd/testoidcauth/main.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os"
1414
"path"
1515
"reflect"
16+
"strings"
1617
"sync"
1718
"time"
1819
"unsafe"
@@ -101,7 +102,7 @@ func main() {
101102

102103
hasError := false
103104
aux := func(test_name string, f func() error) {
104-
fmt.Printf("%s...", test_name)
105+
fmt.Printf("%s...\n", test_name)
105106
err := f()
106107
if err != nil {
107108
fmt.Println("Test Error: ", err)
@@ -148,6 +149,8 @@ func main() {
148149
aux("machine_5_2_azureWithNoUsername", machine52azureWithBadUsername)
149150
case "gcp":
150151
aux("machine_6_1_gcpWithNoUsername", machine61gcpWithNoUsername)
152+
case "k8s":
153+
aux("machine_k8s", machinek8s)
151154
default:
152155
log.Fatal("Unknown OIDC_ENV: ", env)
153156
}
@@ -282,7 +285,7 @@ func machine21validCallbackInputs() error {
282285
tokenFile := tokenFile("test_user1")
283286
accessToken, err := os.ReadFile(tokenFile)
284287
if err != nil {
285-
fmt.Printf("machine_2_1: failed reading token file: %v", err)
288+
return nil, fmt.Errorf("machine_2_1: failed reading token file: %w", err)
286289
}
287290
return &options.OIDCCredential{
288291
AccessToken: string(accessToken),
@@ -1817,3 +1820,27 @@ func machine61gcpWithNoUsername() error {
18171820
}
18181821
return nil
18191822
}
1823+
1824+
// machinek8s tests the "k8s" Kubernetes OIDC environment. There is no specified
1825+
// prose test for "k8s", so this test simply checks that you can run a "find"
1826+
// with the provided conn string.
1827+
func machinek8s() error {
1828+
if !strings.Contains(uriSingle, "ENVIRONMENT:k8s") {
1829+
return fmt.Errorf("expected MONGODB_URI_SINGLE to specify ENVIRONMENT:k8s for Kubernetes test")
1830+
}
1831+
1832+
opts := options.Client().ApplyURI(uriSingle)
1833+
client, err := mongo.Connect(opts)
1834+
if err != nil {
1835+
return fmt.Errorf("machine_k8s: failed connecting client: %v", err)
1836+
}
1837+
defer func() { _ = client.Disconnect(context.Background()) }()
1838+
1839+
coll := client.Database("test").Collection("test")
1840+
1841+
_, err = coll.Find(context.Background(), bson.D{})
1842+
if err != nil {
1843+
return fmt.Errorf("machine_k8s: failed executing Find: %v", err)
1844+
}
1845+
return nil
1846+
}

mongo/options/clientoptions.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,20 +593,22 @@ func (c *ClientOptions) Validate() error {
593593
return fmt.Errorf("cannot set both OIDCMachineCallback and OIDCHumanCallback, only one may be specified")
594594
}
595595
if c.Auth.OIDCHumanCallback == nil && c.Auth.AuthMechanismProperties[auth.AllowedHostsProp] != "" {
596-
return fmt.Errorf("Cannot specify ALLOWED_HOSTS without an OIDCHumanCallback")
596+
return fmt.Errorf("cannot specify ALLOWED_HOSTS without an OIDCHumanCallback")
597597
}
598598
if env, ok := c.Auth.AuthMechanismProperties[auth.EnvironmentProp]; ok {
599599
switch env {
600600
case auth.GCPEnvironmentValue, auth.AzureEnvironmentValue:
601+
if c.Auth.AuthMechanismProperties[auth.ResourceProp] == "" {
602+
return fmt.Errorf("%q must be set for the %s %q", auth.ResourceProp, env, auth.EnvironmentProp)
603+
}
604+
fallthrough
605+
case auth.K8SEnvironmentValue:
601606
if c.Auth.OIDCMachineCallback != nil {
602607
return fmt.Errorf("OIDCMachineCallback cannot be specified with the %s %q", env, auth.EnvironmentProp)
603608
}
604609
if c.Auth.OIDCHumanCallback != nil {
605610
return fmt.Errorf("OIDCHumanCallback cannot be specified with the %s %q", env, auth.EnvironmentProp)
606611
}
607-
if c.Auth.AuthMechanismProperties[auth.ResourceProp] == "" {
608-
return fmt.Errorf("%q must be set for the %s %q", auth.ResourceProp, env, auth.EnvironmentProp)
609-
}
610612
default:
611613
if c.Auth.AuthMechanismProperties[auth.ResourceProp] != "" {
612614
return fmt.Errorf("%q must not be set for the %s %q", auth.ResourceProp, env, auth.EnvironmentProp)

mongo/options/clientoptions_test.go

Lines changed: 3 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -467,107 +467,14 @@ func TestClientOptions(t *testing.T) {
467467
OIDCMachineCallback: emptyCb,
468468
AuthMechanismProperties: map[string]string{"ALLOWED_HOSTS": "www.example.com"},
469469
}),
470-
err: fmt.Errorf("Cannot specify ALLOWED_HOSTS without an OIDCHumanCallback"),
470+
err: fmt.Errorf("cannot specify ALLOWED_HOSTS without an OIDCHumanCallback"),
471471
},
472472
{
473473
name: "cannot set OIDCMachineCallback in GCP Environment",
474474
opts: Client().SetAuth(Credential{
475475
AuthMechanism: "MONGODB-OIDC",
476476
OIDCMachineCallback: emptyCb,
477-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "gcp"},
478-
}),
479-
err: fmt.Errorf(`OIDCMachineCallback cannot be specified with the gcp "ENVIRONMENT"`),
480-
},
481-
{
482-
name: "cannot set OIDCMachineCallback in AZURE Environment",
483-
opts: Client().SetAuth(Credential{
484-
AuthMechanism: "MONGODB-OIDC",
485-
OIDCMachineCallback: emptyCb,
486-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "azure"},
487-
}),
488-
err: fmt.Errorf(`OIDCMachineCallback cannot be specified with the azure "ENVIRONMENT"`),
489-
},
490-
{
491-
name: "TOKEN_RESOURCE must be set in GCP Environment",
492-
opts: Client().SetAuth(Credential{
493-
AuthMechanism: "MONGODB-OIDC",
494-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "gcp"},
495-
}),
496-
err: fmt.Errorf(`"TOKEN_RESOURCE" must be set for the gcp "ENVIRONMENT"`),
497-
},
498-
{
499-
name: "TOKEN_RESOURCE must be set in AZURE Environment",
500-
opts: Client().SetAuth(Credential{
501-
AuthMechanism: "MONGODB-OIDC",
502-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "azure"},
503-
}),
504-
err: fmt.Errorf(`"TOKEN_RESOURCE" must be set for the azure "ENVIRONMENT"`),
505-
},
506-
{
507-
name: "TOKEN_RESOURCE must not be set in TEST Environment",
508-
opts: Client().SetAuth(Credential{
509-
AuthMechanism: "MONGODB-OIDC",
510-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "test", "TOKEN_RESOURCE": "stuff"},
511-
}),
512-
err: fmt.Errorf(`"TOKEN_RESOURCE" must not be set for the test "ENVIRONMENT"`),
513-
},
514-
{
515-
name: "TOKEN_RESOURCE must not be set in any other Environment",
516-
opts: Client().SetAuth(Credential{
517-
AuthMechanism: "MONGODB-OIDC",
518-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "random env!", "TOKEN_RESOURCE": "stuff"},
519-
}),
520-
err: fmt.Errorf(`"TOKEN_RESOURCE" must not be set for the random env! "ENVIRONMENT"`),
521-
},
522-
}
523-
for _, tc := range testCases {
524-
tc := tc // Capture range variable.
525-
526-
t.Run(tc.name, func(t *testing.T) {
527-
t.Parallel()
528-
529-
err := tc.opts.Validate()
530-
assert.Equal(t, tc.err, err, "want error %v, got error %v", tc.err, err)
531-
})
532-
}
533-
})
534-
t.Run("OIDC auth configuration validation", func(t *testing.T) {
535-
t.Parallel()
536-
537-
emptyCb := func(_ context.Context, _ *OIDCArgs) (*OIDCCredential, error) {
538-
return nil, nil
539-
}
540-
541-
testCases := []struct {
542-
name string
543-
opts *ClientOptions
544-
err error
545-
}{
546-
{
547-
name: "password must not be set",
548-
opts: Client().SetAuth(Credential{AuthMechanism: "MONGODB-OIDC", Password: "password"}),
549-
err: fmt.Errorf("password must not be set for the MONGODB-OIDC auth mechanism"),
550-
},
551-
{
552-
name: "cannot set both OIDCMachineCallback and OIDCHumanCallback simultaneously",
553-
opts: Client().SetAuth(Credential{AuthMechanism: "MONGODB-OIDC",
554-
OIDCMachineCallback: emptyCb, OIDCHumanCallback: emptyCb}),
555-
err: fmt.Errorf("cannot set both OIDCMachineCallback and OIDCHumanCallback, only one may be specified"),
556-
},
557-
{
558-
name: "cannot set ALLOWED_HOSTS without OIDCHumanCallback",
559-
opts: Client().SetAuth(Credential{AuthMechanism: "MONGODB-OIDC",
560-
OIDCMachineCallback: emptyCb,
561-
AuthMechanismProperties: map[string]string{"ALLOWED_HOSTS": "www.example.com"},
562-
}),
563-
err: fmt.Errorf("Cannot specify ALLOWED_HOSTS without an OIDCHumanCallback"),
564-
},
565-
{
566-
name: "cannot set OIDCMachineCallback in GCP Environment",
567-
opts: Client().SetAuth(Credential{
568-
AuthMechanism: "MONGODB-OIDC",
569-
OIDCMachineCallback: emptyCb,
570-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "gcp"},
477+
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "gcp", "TOKEN_RESOURCE": "stuff"},
571478
}),
572479
err: fmt.Errorf(`OIDCMachineCallback cannot be specified with the gcp "ENVIRONMENT"`),
573480
},
@@ -576,7 +483,7 @@ func TestClientOptions(t *testing.T) {
576483
opts: Client().SetAuth(Credential{
577484
AuthMechanism: "MONGODB-OIDC",
578485
OIDCMachineCallback: emptyCb,
579-
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "azure"},
486+
AuthMechanismProperties: map[string]string{"ENVIRONMENT": "azure", "TOKEN_RESOURCE": "stuff"},
580487
}),
581488
err: fmt.Errorf(`OIDCMachineCallback cannot be specified with the azure "ENVIRONMENT"`),
582489
},

0 commit comments

Comments
 (0)