From c5d57c9710c94b009ca869cce4e3d5343956c65f Mon Sep 17 00:00:00 2001 From: Brandon Squizzato Date: Fri, 15 May 2026 16:46:50 -0400 Subject: [PATCH 1/2] fix(ambient-api-server): runAsNonRoot compliance and migration ID ordering Add runAsUser: 1000 to pod securityContext and bake a non-root appuser into the Dockerfile so the image satisfies the runAsNonRoot requirement without relying solely on the manifest override. Fix typedFKMigration ID from 202505130001 to 202605150001 so it sorts after the 202603100138 migration that creates the role_bindings table; the old ID caused fresh-cluster deployments to fail with "relation role_bindings does not exist". Co-Authored-By: Claude Sonnet 4.6 --- components/ambient-api-server/Dockerfile | 7 ++++++- .../ambient-api-server/plugins/roleBindings/migration.go | 2 +- .../manifests/base/core/ambient-api-server-service.yml | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/ambient-api-server/Dockerfile b/components/ambient-api-server/Dockerfile index fb32013ac..9f322501b 100755 --- a/components/ambient-api-server/Dockerfile +++ b/components/ambient-api-server/Dockerfile @@ -23,11 +23,16 @@ ARG GIT_COMMIT=unknown RUN \ microdnf install -y \ util-linux \ + shadow-utils \ && \ - microdnf clean all + microdnf clean all \ + && \ + useradd -u 1000 -M -s /sbin/nologin -r appuser COPY --from=builder /workspace/ambient-api-server /usr/local/bin/ +USER 1000 + EXPOSE 8000 ENTRYPOINT ["/usr/local/bin/ambient-api-server", "serve"] diff --git a/components/ambient-api-server/plugins/roleBindings/migration.go b/components/ambient-api-server/plugins/roleBindings/migration.go index 677066a77..dffd525f3 100644 --- a/components/ambient-api-server/plugins/roleBindings/migration.go +++ b/components/ambient-api-server/plugins/roleBindings/migration.go @@ -32,7 +32,7 @@ func migration() *gormigrate.Migration { func typedFKMigration() *gormigrate.Migration { return &gormigrate.Migration{ - ID: "202505130001", + ID: "202605150001", Migrate: func(tx *gorm.DB) error { // Drop the old unique index that depends on scope_id before altering columns if err := tx.Exec(`DROP INDEX IF EXISTS idx_binding_lookup`).Error; err != nil { diff --git a/components/manifests/base/core/ambient-api-server-service.yml b/components/manifests/base/core/ambient-api-server-service.yml index efce7ca67..d9658dd32 100644 --- a/components/manifests/base/core/ambient-api-server-service.yml +++ b/components/manifests/base/core/ambient-api-server-service.yml @@ -34,6 +34,7 @@ spec: serviceAccountName: ambient-api-server securityContext: runAsNonRoot: true + runAsUser: 1000 seccompProfile: type: RuntimeDefault initContainers: From aad7babea2ae14971bb408c2c593462a89a73cb3 Mon Sep 17 00:00:00 2001 From: Brandon Squizzato Date: Fri, 15 May 2026 16:51:06 -0400 Subject: [PATCH 2/2] fix(backend): propagate auth errors from K8s list in ListSessions Return 401/403 to the caller when the K8s dynamic client gets an Unauthorized or Forbidden error during session listing, rather than masking both as 500. Applies to both the initial List call and the paginated continue call. Co-Authored-By: Claude Sonnet 4.6 --- components/backend/handlers/sessions.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/components/backend/handlers/sessions.go b/components/backend/handlers/sessions.go index a97a03601..1b1f372ae 100755 --- a/components/backend/handlers/sessions.go +++ b/components/backend/handlers/sessions.go @@ -639,7 +639,13 @@ func ListSessions(c *gin.Context) { list, err := k8sDyn.Resource(gvr).Namespace(project).List(ctx, listOpts) if err != nil { log.Printf("Failed to list agentic sessions in project %s: %v", project, err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list agentic sessions"}) + if errors.IsUnauthorized(err) { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or invalid"}) + } else if errors.IsForbidden(err) { + c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized to access project"}) + } else { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list agentic sessions"}) + } return } @@ -650,7 +656,13 @@ func ListSessions(c *gin.Context) { list, err = k8sDyn.Resource(gvr).Namespace(project).List(ctx, listOpts) if err != nil { log.Printf("Failed to list agentic sessions (continue) in project %s: %v", project, err) - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list agentic sessions"}) + if errors.IsUnauthorized(err) { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or invalid"}) + } else if errors.IsForbidden(err) { + c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized to access project"}) + } else { + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list agentic sessions"}) + } return } allItems = append(allItems, list.Items...)