feat: add Connect/Workbench PPM authenticated repos via Identity Federation#114
feat: add Connect/Workbench PPM authenticated repos via Identity Federation#114ian-flores wants to merge 15 commits intomainfrom
Conversation
Enable Connect and Workbench to authenticate against PPM using Kubernetes Identity Federation (RFC 8693 token exchange). Adds OIDC and Identity Federation config types for PPM, shared token exchange init container and sidecar helpers, and opt-in AuthenticatedRepos flag on Connect/Workbench specs.
All 8 findings addressed. Build passes and tests pass. Here's the summary: Changes: - Install curl and jq in token exchange script (`apk add --no-cache`) so alpine:3 default image works - Add null/empty token validation after jq extraction to fail fast instead of writing "null" as password - Add `OIDCAudience` field to `SiteSpec` so OIDC audience is configurable (defaults to `sts.amazonaws.com` for backwards compatibility) - Revert `AutomountServiceAccountToken` to `ptr.To(false)` in Connect — projected volume works independently - Add `PPMAuthImage` to `InternalConnectSpec` and `InternalWorkbenchSpec` and propagate from Site controllers - Add gcfg injection validation for `IdentityFederation` Name (reject `"`, `]`, newlines) - Add `cleanupPPMAuthConfigMap` to delete the ConfigMap when authenticated repos feature is disabled - Add `SanitizePPMUrl` helper to strip existing scheme before prepending `https://`, preventing double-prefix
- Fix audience mismatch: thread OIDCAudience from site spec through Connect/Workbench to projected SA token volumes - Remove runtime apk add from token exchange script (image must have curl+jq pre-installed) - Add sidecar resilience: catch refresh failures instead of dying - Remove unused ppmAuthCurlrcPath constant - Clarify AutomountServiceAccountToken comment re: projected volumes - Add .claude/tsc-cache to .gitignore
Add typed support for all PPM authentication configuration fields: - New [Authentication] section (APITokenAuth, DeviceAuthType, session lifetime, etc.) - 12 new [OpenIDConnect] fields (ClientSecretFile, PKCE, claims customization, token lifetime, etc.) - 9 new [IdentityFederation] fields (claims, groups, roles, logging)
alpine:3 runs as root by default, which fails pod security contexts that require runAsNonRoot. Set RunAsUser=65534 (nobody) on both init and sidecar containers. PPM requires Scope, RoleClaim, or GroupToScopeMapping when OIDC is configured. Default to "repos:read:*" in the site controller.
alpine:3 doesn't include curl or jq, and apk install requires root. Replace curl with wget (BusyBox built-in) and jq with a sed-based JSON field extractor for zero-dependency operation.
The field was tagged json:"-" which prevented it from being stored in the PackageManager CR. The site controller sets it in memory but it was lost when the PM controller read the CR back from the API.
The Name field was tagged json:"-" so it was lost during Kubernetes API serialization. This caused empty section names in the generated gcfg: [IdentityFederation ""] instead of [IdentityFederation "connect"].
PPM treats ClientSecret as a literal value, not a file path. Use ClientSecretFile instead so PPM reads the secret from the mounted file. K8s SA tokens don't have a preferred_username claim. Set UniqueIdClaim and UsernameClaim to "sub" for Identity Federation entries.
All builds pass and tests are green. Findings #1 (PPMAuthImage propagation), #2 (cache files), and #3 (apk add stderr) were already addressed in prior commits. I fixed the two remaining issues. Changes: - `SanitizePPMUrl` now returns empty string for empty input instead of invalid `"https://"` - Added edge case tests for `SanitizePPMUrl`: empty string and URL with port/path
|
Claude finished @ian-flores's task —— View job Review: PPM Authenticated Repos via Identity FederationWell-structured feature with good separation of concerns. The auth flow (projected SA token → init container → PPM token exchange → netrc/curlrc) is sound. Here are findings organized by severity. Security (Elevated Scrutiny)1. SA token visible in process args — 2. Error logging leaks response data — 3. Validation Gaps4. Missing validation: 5. 6. EKS-specific default audience — Code Quality7. Unused parameter 8. Manual field serialization for IdentityFederation — 9. Inconsistent JSON tag casing — 10. Duplicated PPM auth setup logic — API Design11. Minor12. Cleanup doesn't check ownership — What looks good
|
Description
Enable Connect and Workbench to authenticate against PPM using Kubernetes Identity Federation (RFC 8693 token exchange). When
authenticatedRepos: trueis set on a product spec, the operator automatically injects init container + sidecar pods that exchange K8s projected service account tokens for PPM API tokens, writing netrc/curlrc files for Python and R package installation.Issue
Dependencies
Code Flow
Authentication Flow
Key Components
PPM Identity Federation Config (
package_manager_config.go):PackageManagerIdentityFederationConfigstruct with full field coverage. ExtendedGenerateGcfg()to emit named[IdentityFederation "connect"]sections.Token Exchange Helpers (
ppm_auth.go): Shared functions for init container, sidecar container, volumes, volume mounts, and env vars. Shell script (mounted from ConfigMap) uses wget + sed (BusyBox built-ins in alpine:3) for zero-dependency operation.Product Integration (
connect.go,workbench.go): WhenAuthenticatedRepos=true, adds projected SA token volume, shared emptyDir, script ConfigMap volume, init container, sidecar container, NETRC + CURL_HOME env vars.Site Controller (
site_controller.go): Creates{site}-ppm-auth-scriptConfigMap when any product has authenticated repos enabled. Auto-configures Identity Federation entries on PPM based on product flags.Testing
Tested end-to-end on
ganso01-stagingwith adhoc imageadhoc-ppm-auth-repos-v1.16.1-15-gedc6aaa./mnt/ppm-auth/netrccontains valid JWT with correct subject claim/mnt/ppm-auth/.curlrccontains--netrc-filedirectiveNETRCandCURL_HOMEenv vars set on main product containersauth_available: trueandsso_enabled: trueafter enabling repo authCategory of change