|
4 | 4 | "context" |
5 | 5 | "fmt" |
6 | 6 | "os" |
| 7 | + "strings" |
7 | 8 | "time" |
8 | 9 |
|
9 | 10 | appsv1 "k8s.io/api/apps/v1" |
@@ -201,10 +202,61 @@ func (*VirtualMCPServerReconciler) buildEnvVarsForVmcp( |
201 | 202 | // Log level env var will be added here |
202 | 203 | } |
203 | 204 |
|
204 | | - // Note: Secrets (OIDC client secrets, Redis passwords, service account credentials) are NOT added |
205 | | - // as environment variables here. They are validated by validateSecretReferences() during reconciliation |
206 | | - // and configured through the vmcp config file or volume mounts for better security. |
207 | | - // Environment variables are reserved for non-sensitive configuration only. |
| 205 | + // Mount OIDC client secret as environment variable |
| 206 | + // The vmcp config file will reference this via client_secret_env: "VMCP_OIDC_CLIENT_SECRET" |
| 207 | + // |
| 208 | + // Two approaches are supported: |
| 209 | + // 1. ClientSecretRef: References an existing Kubernetes Secret (recommended) |
| 210 | + // 2. ClientSecret: Literal value that will be stored in a generated Secret |
| 211 | + // |
| 212 | + // Both cases result in the secret being mounted as an environment variable for security. |
| 213 | + if vmcp.Spec.IncomingAuth != nil && |
| 214 | + vmcp.Spec.IncomingAuth.OIDCConfig != nil && |
| 215 | + vmcp.Spec.IncomingAuth.OIDCConfig.Inline != nil { |
| 216 | + inline := vmcp.Spec.IncomingAuth.OIDCConfig.Inline |
| 217 | + |
| 218 | + // For testing: Skip OIDC discovery for example/test issuers |
| 219 | + // This allows tests to run without requiring a real OIDC provider |
| 220 | + if inline.Issuer != "" && (strings.Contains(inline.Issuer, "example.com") || strings.Contains(inline.Issuer, "test")) { |
| 221 | + env = append(env, corev1.EnvVar{ |
| 222 | + Name: "VMCP_SKIP_OIDC_DISCOVERY", |
| 223 | + Value: "true", |
| 224 | + }) |
| 225 | + } |
| 226 | + |
| 227 | + if inline.ClientSecretRef != nil { |
| 228 | + // Approach 1: Mount from existing Secret reference |
| 229 | + env = append(env, corev1.EnvVar{ |
| 230 | + Name: "VMCP_OIDC_CLIENT_SECRET", |
| 231 | + ValueFrom: &corev1.EnvVarSource{ |
| 232 | + SecretKeyRef: &corev1.SecretKeySelector{ |
| 233 | + LocalObjectReference: corev1.LocalObjectReference{ |
| 234 | + Name: inline.ClientSecretRef.Name, |
| 235 | + }, |
| 236 | + Key: inline.ClientSecretRef.Key, |
| 237 | + }, |
| 238 | + }, |
| 239 | + }) |
| 240 | + } else if inline.ClientSecret != "" { |
| 241 | + // Approach 2: Mount from generated Secret containing literal value |
| 242 | + // The generated secret is created by ensureOIDCClientSecret() |
| 243 | + generatedSecretName := fmt.Sprintf("%s-oidc-client-secret", vmcp.Name) |
| 244 | + env = append(env, corev1.EnvVar{ |
| 245 | + Name: "VMCP_OIDC_CLIENT_SECRET", |
| 246 | + ValueFrom: &corev1.EnvVarSource{ |
| 247 | + SecretKeyRef: &corev1.SecretKeySelector{ |
| 248 | + LocalObjectReference: corev1.LocalObjectReference{ |
| 249 | + Name: generatedSecretName, |
| 250 | + }, |
| 251 | + Key: "clientSecret", |
| 252 | + }, |
| 253 | + }, |
| 254 | + }) |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + // Note: Other secrets (Redis passwords, service account credentials) may be added here in the future |
| 259 | + // following the same pattern of mounting from Kubernetes Secrets as environment variables. |
208 | 260 |
|
209 | 261 | return ctrlutil.EnsureRequiredEnvVars(ctx, env) |
210 | 262 | } |
|
0 commit comments