Skip to content

Issue: Add Pod Security Standards (PSA) Support to PostgreSQL CR Spec #3158

Description

@ayushnigam0001

Title: Add Pod Security Standards (PSA) Fields Support in postgresql CR spec for v2.0.1+

Repository: https://github.com/zalando/postgres-operator
Version: postgres-operator v2.0.1 (and v2.0.0)
Date: 2026-08-03


Problem Statement

postgres-operator v2.0.1 does not support Pod Security Standards (PSA) Restricted profile fields in the postgresql Custom Resource Definition (CRD). This prevents compliance with Kubernetes PSA enforcement policies like Kyverno's psa-validation-by-pod/restricted.

The operator moved PSA configuration from OperatorConfiguration (v1.x) to individual postgresql CR specs (v2.x), but the CRD schema was not updated to include the necessary fields for PSA compliance.


Missing Fields in postgresql CR Schema

The following PSA-related fields are defined in the Spilo container specification but are missing from the postgresql CRD schema:

1. Spilo Container PSA Fields

spiloRunAsNonRoot:
  type: boolean
  description: "Enforce non-root execution for spilo container (PSA Restricted requirement)"

spiloAllowPrivilegeEscalation:
  type: boolean
  description: "Prevent privilege escalation in spilo container (PSA Restricted requirement)"

droppedPodCapabilities:
  type: array
  items:
    type: string
  description: "Linux capabilities to drop from spilo container (PSA Restricted: ALL)"

spiloSeccompProfile:
  type: object
  properties:
    type:
      type: string
    localhostProfile:
      type: string
  description: "Seccomp profile for spilo container (PSA Restricted: RuntimeDefault)"

2. Sidecar Container PSA Fields (in sidecars[].securityContext)

sidecars:
  - name: exporter
    securityContext:
      allowPrivilegeEscalation:
        type: boolean
      capabilities:
        type: object
        properties:
          add:
            type: array
            items: string
          drop:
            type: array
            items: string
      readOnlyRootFilesystem:
        type: boolean
      runAsNonRoot:
        type: boolean
      runAsUser:
        type: integer
        format: int64
      seccompProfile:
        type: object
        properties:
          type:
            type: string
          localhostProfile:
            type: string

Root Cause

In v2.0.1, the operator development team:

  1. ✅ Moved PSA configuration from OperatorConfiguration to individual postgresql CR specs
  2. ✅ Updated the Go type definitions in postgresql_types.go
  3. Did NOT update the CRD YAML schema to reflect these new fields

When CRD schemas lack field definitions, Kubernetes API server silently prunes unknown fields during validation, breaking GitOps workflows and causing infinite sync loops.

Reference: Kubernetes 1.25+ applies strict CRD schema validation. See CRD Validation Rules.


Impact

Without PSA Field Support:

  1. Kyverno Policy Violations: Pods fail PSA Restricted validation

    policy psa-validation-by-pod/restricted fail:
    - allowPrivilegeEscalation != false
    - runAsNonRoot != true
    - capabilities not dropped
    - seccompProfile not set
    
  2. ArgoCD Infinite OutOfSync: PSA fields in postgresql CR are rejected by API server validation

    error: error validating data: ValidationError(PostgreSQL.spec):
    unknown field "spiloRunAsNonRoot" in io.acid.zalan.do.v1.PostgreSQL.spec
    
  3. Manual CRD Patches Required: Teams must manually patch the CRD to add field definitions

    kubectl patch crd postgresqls.acid.zalan.do --type merge \
      -p '{"spec":{"versions":[{"schema":{"openAPIV3Schema":{"properties":{"spec":{"properties":{"spiloRunAsNonRoot":{"type":"boolean"}}}}}}}]}}'

Proof of Missing Fields

Current CRD status (v2.0.1):

  • spiloRunAsUser ✅ Defined
  • spiloRunAsGroup ✅ Defined
  • spiloRunAsNonRootMissing
  • spiloAllowPrivilegeEscalationMissing
  • droppedPodCapabilitiesMissing
  • spiloSeccompProfileMissing
  • sidecars[].securityContextMissing (only name, image, env defined)

Kubernetes API Server Behavior:

$ kubectl apply -f postgresql-with-psa.yaml
error: error validating "postgresql.yaml": error validating data:
  ValidationError(PostgreSQL.spec.spiloRunAsNonRoot): unknown field "spiloRunAsNonRoot"...

Use Case: Kubernetes PSA Enforcement

With Kubernetes Pod Security Standards and tools like Kyverno, clusters enforce security policies:

# Kyverno Policy (Restricted Profile)
validationFailureAction: enforce
rules:
  - name: require-non-root
    validation:
      message: "Running as root is not allowed"
      pattern:
        spec:
          =(containers):
            - securityContext:
                runAsNonRoot: true
          =(initContainers):
            - securityContext:
                runAsNonRoot: true

Without PSA field support in the postgresql CR, pods cannot be deployed to clusters with enforced PSA policies.


Expected Behavior

Users should be able to define PSA compliance in postgresql CR:

apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: my-db
  namespace: default
spec:
  numberOfInstances: 3
  
  # PSA Restricted Compliance
  spiloRunAsNonRoot: true
  spiloAllowPrivilegeEscalation: false
  droppedPodCapabilities:
    - ALL
  spiloSeccompProfile:
    type: RuntimeDefault
  
  sidecars:
    - name: exporter
      image: prometheuscommunity/postgres-exporter:latest
      securityContext:
        runAsNonRoot: true
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL
        seccompProfile:
          type: RuntimeDefault

This should validate without manual CRD patches.


Actual Behavior

Currently, applying the same postgresql CR results in:

  • ✅ Operator accepts the CR (Go types support it)
  • ❌ Kubernetes API rejects validation (CRD schema missing)
  • ❌ Fields are silently pruned from spec
  • ❌ Pod security is NOT applied
  • ❌ Kyverno policy violations occur

Workaround (Temporary)

Teams must manually patch the CRD to add PSA field definitions:

# Temporary Patch - Not Recommended for Production
kubectl apply --server-side --force-conflicts -f postgresqls-extended-crd.yaml

This workaround:

  • ❌ Breaks GitOps automation
  • ❌ Requires manual intervention on every cluster
  • ❌ Gets overwritten on operator upgrades
  • ❌ Is not documented in operator release notes

Proposed Solution

Update the postgres-operator v2.0.1+ CRD YAML generation to include PSA fields in the postgresql CR schema:

Location: pkg/apis/acid.zalan.do/v1/postgresql_types.go

Add struct tags for CRD schema generation:

type PostgresSpec struct {
  // ... existing fields ...

  // PSA Restricted Compliance
  SpiloRunAsNonRoot *bool `json:"spiloRunAsNonRoot,omitempty"`
  SpiloAllowPrivilegeEscalation *bool `json:"spiloAllowPrivilegeEscalation,omitempty"`
  DroppedPodCapabilities []string `json:"droppedPodCapabilities,omitempty"`
  SpiloSeccompProfile *SeccompProfile `json:"spiloSeccompProfile,omitempty"`
}

type SeccompProfile struct {
  Type string `json:"type,omitempty"`
  LocalhostProfile *string `json:"localhostProfile,omitempty"`
}

Rebuild CRD:

make generate-crd
# This regenerates crds/postgresqls.yaml with the new fields

Related Issues & PRs


Affected Versions

  • postgres-operator v2.0.0: ❌ Missing
  • postgres-operator v2.0.1: ❌ Missing
  • postgres-operator v1.14.0: ✅ PSA in OperatorConfiguration (deprecated approach)

Testing Recommendations

  1. Unit Test: Verify CRD schema includes all PSA fields

    kubectl apply -f test-postgresql-with-psa.yaml --dry-run=server
    # Should validate without errors
  2. E2E Test: Verify pods render with PSA security context

    kubectl get pod <pod> -o jsonpath='{.spec.containers[0].securityContext}'
    # Should show: {"runAsNonRoot":true,"allowPrivilegeEscalation":false,...}
  3. Kyverno Integration Test: Verify compliance with PSA Restricted policy

    # Deploy with Kyverno enforce mode
    # Postgres pods should pass validation

References


Priority

High — Blocks deployment to production Kubernetes clusters with PSA enforcement enabled.


Suggested Labels

  • type/feature-request
  • area/crd
  • area/security
  • component/v2.0
  • priority/high

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions