Skip to content

Commit 5df3e5c

Browse files
authored
Merge branch 'master' into feature/mk-oidc-crd-propagation
2 parents 7257b4e + f0f8eaa commit 5df3e5c

File tree

9 files changed

+227
-14
lines changed

9 files changed

+227
-14
lines changed

.github/dependabot.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,27 @@ updates:
55
schedule:
66
interval: weekly
77
day: monday
8+
groups:
9+
go-deps:
10+
applies-to: "version-updates"
11+
patterns:
12+
- "*"
813
ignore:
914
- dependency-name: k8s.io/api
1015
- dependency-name: k8s.io/apimachinery
1116
- dependency-name: k8s.io/client-go
1217
- dependency-name: k8s.io/code-generator
1318
- dependency-name: sigs.k8s.io/controller-runtime
19+
1420
- package-ecosystem: pip
1521
directory: "/"
1622
schedule:
1723
interval: weekly
1824
day: monday
25+
groups:
26+
pip-deps:
27+
applies-to: "version-updates"
28+
patterns:
29+
- "*"
30+
ignore:
31+
- dependency-name: kubernetes

api/v1/mdb/mongodb_validation.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
113113

114114
authentication := db.Security.Authentication
115115
validators = append(validators, oidcAuthModeValidator(authentication))
116+
validators = append(validators, oidcAuthRequiresEnterprise)
116117

117118
providerConfigs := authentication.OIDCProviderConfigs
118119
if len(providerConfigs) == 0 {
@@ -122,6 +123,7 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
122123
validators = append(validators,
123124
oidcProviderConfigsUniqueNameValidation(providerConfigs),
124125
oidcProviderConfigsSingleWorkforceIdentityFederationValidation(providerConfigs),
126+
oidcProviderConfigUniqueIssuerURIValidation(providerConfigs),
125127
)
126128

127129
for _, config := range providerConfigs {
@@ -130,13 +132,58 @@ func oidcAuthValidators(db DbCommonSpec) []func(DbCommonSpec) v1.ValidationResul
130132
oidcProviderConfigClientIdValidator(config),
131133
oidcProviderConfigRequestedScopesValidator(config),
132134
oidcProviderConfigAuthorizationTypeValidator(config),
133-
oidcAuthRequiresEnterprise,
134135
)
135136
}
136137

137138
return validators
138139
}
139140

141+
// oidcProviderConfigUniqueIssuerURIValidation is based on the documentation here:
142+
// https://www.mongodb.com/docs/manual/reference/parameters/#oidcidentityproviders-fields
143+
func oidcProviderConfigUniqueIssuerURIValidation(configs []OIDCProviderConfig) func(DbCommonSpec) v1.ValidationResult {
144+
return func(d DbCommonSpec) v1.ValidationResult {
145+
if len(configs) == 0 {
146+
return v1.ValidationSuccess()
147+
}
148+
149+
// Check if version supports duplicate issuers (7.0, 7.3, or 8.0+)
150+
versionParts := strings.Split(strings.TrimSuffix(d.Version, "-ent"), ".")
151+
supportsMultipleIssuers := false
152+
if len(versionParts) >= 2 {
153+
major := versionParts[0]
154+
minor := versionParts[1]
155+
if major == "8" || (major == "7" && (minor == "0" || minor == "3")) {
156+
supportsMultipleIssuers = true
157+
}
158+
}
159+
160+
if supportsMultipleIssuers {
161+
// Track issuer+audience combinations
162+
issuerAudienceCombos := make(map[string]string)
163+
for _, config := range configs {
164+
comboKey := config.IssuerURI + ":" + config.Audience
165+
if previousConfig, exists := issuerAudienceCombos[comboKey]; exists {
166+
return v1.ValidationWarning("OIDC provider configs %q and %q have duplicate IssuerURI and Audience combination",
167+
previousConfig, config.ConfigurationName)
168+
}
169+
issuerAudienceCombos[comboKey] = config.ConfigurationName
170+
}
171+
} else {
172+
// For older versions, require unique issuers
173+
uris := make(map[string]string)
174+
for _, config := range configs {
175+
if previousConfig, exists := uris[config.IssuerURI]; exists {
176+
return v1.ValidationError("OIDC provider configs %q and %q have duplicate IssuerURI: %s",
177+
previousConfig, config.ConfigurationName, config.IssuerURI)
178+
}
179+
uris[config.IssuerURI] = config.ConfigurationName
180+
}
181+
}
182+
183+
return v1.ValidationSuccess()
184+
}
185+
}
186+
140187
func oidcAuthModeValidator(authentication *Authentication) func(DbCommonSpec) v1.ValidationResult {
141188
return func(spec DbCommonSpec) v1.ValidationResult {
142189
// OIDC cannot be used for agent authentication so other auth mode has to enabled as well

api/v1/mdb/mongodb_validation_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,131 @@ func TestOIDCAuthValidation(t *testing.T) {
497497
})
498498
}
499499
}
500+
501+
func TestOIDCProviderConfigUniqueIssuerURIValidation(t *testing.T) {
502+
tests := []struct {
503+
name string
504+
mongoVersion string
505+
configs []OIDCProviderConfig
506+
expectedResult v1.ValidationResult
507+
}{
508+
{
509+
name: "MongoDB 6.0 with duplicate issuer URIs - error",
510+
mongoVersion: "6.0.0",
511+
configs: []OIDCProviderConfig{
512+
{
513+
ConfigurationName: "config1",
514+
IssuerURI: "https://provider.com",
515+
Audience: "audience1",
516+
},
517+
{
518+
ConfigurationName: "config2",
519+
IssuerURI: "https://provider.com",
520+
Audience: "audience2",
521+
},
522+
},
523+
expectedResult: v1.ValidationError("OIDC provider configs %q and %q have duplicate IssuerURI: %s",
524+
"config1", "config2", "https://provider.com"),
525+
},
526+
{
527+
name: "MongoDB 7.0 with unique issuer+audience combinations",
528+
mongoVersion: "7.0.0",
529+
configs: []OIDCProviderConfig{
530+
{
531+
ConfigurationName: "config1",
532+
IssuerURI: "https://provider.com",
533+
Audience: "audience1",
534+
},
535+
{
536+
ConfigurationName: "config2",
537+
IssuerURI: "https://provider.com",
538+
Audience: "audience2",
539+
},
540+
},
541+
expectedResult: v1.ValidationSuccess(),
542+
},
543+
{
544+
name: "MongoDB 7.0 with duplicate issuer+audience combinations - warning",
545+
mongoVersion: "7.0.0",
546+
configs: []OIDCProviderConfig{
547+
{
548+
ConfigurationName: "config1",
549+
IssuerURI: "https://provider.com",
550+
Audience: "audience1",
551+
},
552+
{
553+
ConfigurationName: "config2",
554+
IssuerURI: "https://provider.com",
555+
Audience: "audience1",
556+
},
557+
},
558+
expectedResult: v1.ValidationWarning("OIDC provider configs %q and %q have duplicate IssuerURI and Audience combination",
559+
"config1", "config2"),
560+
},
561+
{
562+
name: "MongoDB 7.3 with unique issuer+audience combinations",
563+
mongoVersion: "7.3.0",
564+
configs: []OIDCProviderConfig{
565+
{
566+
ConfigurationName: "config1",
567+
IssuerURI: "https://provider.com",
568+
Audience: "audience1",
569+
},
570+
{
571+
ConfigurationName: "config2",
572+
IssuerURI: "https://provider.com",
573+
Audience: "audience2",
574+
},
575+
},
576+
expectedResult: v1.ValidationSuccess(),
577+
},
578+
{
579+
name: "MongoDB 8.0 with unique issuer+audience combinations",
580+
mongoVersion: "8.0.0",
581+
configs: []OIDCProviderConfig{
582+
{
583+
ConfigurationName: "config1",
584+
IssuerURI: "https://provider.com",
585+
Audience: "audience1",
586+
},
587+
{
588+
ConfigurationName: "config2",
589+
IssuerURI: "https://provider.com",
590+
Audience: "audience2",
591+
},
592+
},
593+
expectedResult: v1.ValidationSuccess(),
594+
},
595+
{
596+
name: "MongoDB enterprise version with -ent suffix",
597+
mongoVersion: "7.0.0-ent",
598+
configs: []OIDCProviderConfig{
599+
{
600+
ConfigurationName: "config1",
601+
IssuerURI: "https://provider.com",
602+
Audience: "audience1",
603+
},
604+
{
605+
ConfigurationName: "config2",
606+
IssuerURI: "https://provider.com",
607+
Audience: "audience2",
608+
},
609+
},
610+
expectedResult: v1.ValidationSuccess(),
611+
},
612+
}
613+
614+
for _, tt := range tests {
615+
t.Run(tt.name, func(t *testing.T) {
616+
validationFunc := oidcProviderConfigUniqueIssuerURIValidation(tt.configs)
617+
618+
dbSpec := DbCommonSpec{
619+
Version: tt.mongoVersion,
620+
}
621+
622+
result := validationFunc(dbSpec)
623+
624+
assert.Equal(t, tt.expectedResult, result)
625+
})
626+
}
627+
}

docker/mongodb-kubernetes-tests/kubetester/certs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -804,16 +804,16 @@ def create_x509_agent_tls_certs(issuer: str, namespace: str, name: str, secret_b
804804

805805
def approve_certificate(name: str) -> None:
806806
"""Approves the CertificateSigningRequest with the provided name"""
807-
body = client.CertificatesV1beta1Api().read_certificate_signing_request_status(name)
808-
conditions = client.V1beta1CertificateSigningRequestCondition(
807+
body = client.CertificatesV1Api().read_certificate_signing_request_status(name)
808+
conditions = client.V1CertificateSigningRequestCondition(
809809
last_update_time=datetime.now(timezone.utc).astimezone(),
810810
message="This certificate was approved by E2E testing framework",
811811
reason="E2ETestingFramework",
812812
type="Approved",
813813
)
814814

815815
body.status.conditions = [conditions]
816-
client.CertificatesV1beta1Api().replace_certificate_signing_request_approval(name, body)
816+
client.CertificatesV1Api().replace_certificate_signing_request_approval(name, body)
817817

818818

819819
def create_x509_user_cert(issuer: str, namespace: str, path: str):
@@ -876,7 +876,7 @@ def yield_existing_csrs(csr_names: List[str], timeout: int = 300) -> Generator[s
876876
while len(csr_names) > 0 and time.time() < stop_time:
877877
csr = random.choice(csr_names)
878878
try:
879-
client.CertificatesV1beta1Api().read_certificate_signing_request_status(csr)
879+
client.CertificatesV1Api().read_certificate_signing_request_status(csr)
880880
except ApiException:
881881
time.sleep(3)
882882
continue

docker/mongodb-kubernetes-tests/kubetester/crypto.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def generate_csr(namespace: str, host: str, servicename: str):
5050

5151

5252
def get_pem_certificate(name: str) -> Optional[str]:
53-
body = client.CertificatesV1beta1Api().read_certificate_signing_request_status(name)
53+
body = client.CertificatesV1Api().read_certificate_signing_request_status(name)
5454
if body.status.certificate is None:
5555
return None
5656
return base64.b64decode(body.status.certificate)

docker/mongodb-kubernetes-tests/kubetester/kubetester.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ def clients(name, api_client: Optional[client.ApiClient] = None):
325325
"appsv1": client.AppsV1Api(api_client=api_client),
326326
"storagev1": client.StorageV1Api(api_client=api_client),
327327
"customv1": client.CustomObjectsApi(api_client=api_client),
328-
"certificates": client.CertificatesV1beta1Api(api_client=api_client),
328+
"certificates": client.CertificatesV1Api(api_client=api_client),
329329
"namespace": KubernetesTester.get_namespace(),
330330
}[name]
331331

@@ -807,7 +807,7 @@ def setup_method(self):
807807
self.client = client
808808
self.corev1 = client.CoreV1Api()
809809
self.appsv1 = client.AppsV1Api()
810-
self.certificates = client.CertificatesV1beta1Api()
810+
self.certificates = client.CertificatesV1Api()
811811
self.customv1 = client.CustomObjectsApi()
812812
self.namespace = KubernetesTester.get_namespace()
813813
self.name = None
@@ -1224,19 +1224,19 @@ def generate_certfile(
12241224
if namespace is None:
12251225
namespace = self.namespace
12261226

1227-
csr_body = client.V1beta1CertificateSigningRequest(
1227+
csr_body = client.V1CertificateSigningRequest(
12281228
metadata=client.V1ObjectMeta(name=csr_name, namespace=namespace),
1229-
spec=client.V1beta1CertificateSigningRequestSpec(
1229+
spec=client.V1CertificateSigningRequestSpec(
12301230
groups=["system:authenticated"],
12311231
usages=["digital signature", "key encipherment", "client auth"],
12321232
request=encoded_request,
12331233
),
12341234
)
12351235

1236-
client.CertificatesV1beta1Api().create_certificate_signing_request(csr_body)
1236+
client.CertificatesV1Api().create_certificate_signing_request(csr_body)
12371237
self.approve_certificate(csr_name)
12381238
wait_for_certs_to_be_issued([csr_name])
1239-
csr = client.CertificatesV1beta1Api().read_certificate_signing_request(csr_name)
1239+
csr = client.CertificatesV1Api().read_certificate_signing_request(csr_name)
12401240
certificate = b64decode(csr.status.certificate)
12411241

12421242
tmp = tempfile.NamedTemporaryFile()
@@ -1383,7 +1383,7 @@ def get_csr_sans(csr_name: str) -> List[str]:
13831383
Return all of the subject alternative names for a given Kubernetes
13841384
certificate signing request.
13851385
"""
1386-
csr = client.CertificatesV1beta1Api().read_certificate_signing_request_status(csr_name)
1386+
csr = client.CertificatesV1Api().read_certificate_signing_request_status(csr_name)
13871387
base64_csr_request = csr.spec.request
13881388
csr_pem_string = b64decode(base64_csr_request)
13891389
csr = x509.load_pem_x509_csr(csr_pem_string, default_backend())

0 commit comments

Comments
 (0)