apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.19.0
  name: openbaoclusters.openbao.org
spec:
  group: openbao.org
  names:
    kind: OpenBaoCluster
    listKind: OpenBaoClusterList
    plural: openbaoclusters
    shortNames:
    - bao
    - baoc
    singular: openbaocluster
  scope: Namespaced
  versions:
  - additionalPrinterColumns:
    - jsonPath: .status.phase
      name: Phase
      type: string
    - jsonPath: .spec.version
      name: Version
      type: string
    - jsonPath: .spec.replicas
      name: Replicas
      type: integer
    - jsonPath: .metadata.creationTimestamp
      name: Age
      type: date
    name: v1alpha1
    schema:
      openAPIV3Schema:
        description: OpenBaoCluster is the Schema for the openbaoclusters API.
        properties:
          apiVersion:
            description: |-
              APIVersion defines the versioned schema of this representation of an object.
              Servers should convert recognized schemas to the latest internal value, and
              may reject unrecognized values.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
            type: string
          kind:
            description: |-
              Kind is a string value representing the REST resource this object represents.
              Servers may infer this from the endpoint the client submits requests to.
              Cannot be updated.
              In CamelCase.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
            type: string
          metadata:
            type: object
          spec:
            description: Spec defines the desired state of OpenBaoCluster.
            properties:
              audit:
                description: |-
                  Audit configures declarative audit devices for the OpenBao cluster.
                  See: https://openbao.org/docs/configuration/audit/
                items:
                  description: |-
                    AuditDevice defines a declarative audit device configuration.
                    See: https://openbao.org/docs/configuration/audit/
                  properties:
                    description:
                      description: Description is an optional description for the
                        audit device.
                      type: string
                    fileOptions:
                      description: |-
                        FileOptions configures options for file audit devices.
                        Only used when Type is "file".
                      properties:
                        filePath:
                          description: |-
                            FilePath is the path to where the audit log will be written.
                            Special keywords: "stdout" writes to standard output, "discard" discards output.
                          minLength: 1
                          type: string
                        mode:
                          description: |-
                            Mode is a string containing an octal number representing the bit pattern for the file mode.
                            Defaults to "0600" if not specified. Set to "0000" to prevent OpenBao from modifying the file mode.
                          type: string
                      required:
                      - filePath
                      type: object
                    httpOptions:
                      description: |-
                        HTTPOptions configures options for HTTP audit devices.
                        Only used when Type is "http".
                      properties:
                        headers:
                          description: |-
                            Headers is a JSON object describing headers. Must take the shape map[string][]string,
                            i.e., an object of headers, with each having one or more values.
                            Headers without values will be ignored.
                          x-kubernetes-preserve-unknown-fields: true
                        uri:
                          description: URI is the URI of the remote server where the
                            audit logs will be written.
                          minLength: 1
                          type: string
                      required:
                      - uri
                      type: object
                    options:
                      description: |-
                        Options contains device-specific configuration options as a map.
                        This is a fallback for backward compatibility and advanced use cases.
                        If structured options (FileOptions, HTTPOptions, etc.) are provided, they take precedence.
                        The structure depends on the audit device type.
                      x-kubernetes-preserve-unknown-fields: true
                    path:
                      description: Path is the path of the audit device in the root
                        namespace.
                      minLength: 1
                      type: string
                    socketOptions:
                      description: |-
                        SocketOptions configures options for socket audit devices.
                        Only used when Type is "socket".
                      properties:
                        address:
                          description: |-
                            Address is the socket server address to use.
                            Example: "127.0.0.1:9090" or "/tmp/audit.sock".
                          type: string
                        socketType:
                          description: |-
                            SocketType is the socket type to use, any type compatible with net.Dial is acceptable.
                            Defaults to "tcp" if not specified.
                          type: string
                        writeTimeout:
                          description: |-
                            WriteTimeout is the (deadline) time in seconds to allow writes to be completed over the socket.
                            A zero value means that write attempts will not time out.
                            Defaults to "2s" if not specified.
                          type: string
                      type: object
                    syslogOptions:
                      description: |-
                        SyslogOptions configures options for syslog audit devices.
                        Only used when Type is "syslog".
                      properties:
                        facility:
                          description: |-
                            Facility is the syslog facility to use.
                            Defaults to "AUTH" if not specified.
                          type: string
                        tag:
                          description: |-
                            Tag is the syslog tag to use.
                            Defaults to "openbao" if not specified.
                          type: string
                      type: object
                    type:
                      description: Type is the type of audit device (e.g., "file",
                        "syslog", "socket", "http").
                      enum:
                      - file
                      - syslog
                      - socket
                      - http
                      minLength: 1
                      type: string
                  required:
                  - path
                  - type
                  type: object
                type: array
              backup:
                description: Backup configures scheduled backups for the cluster.
                properties:
                  image:
                    description: |-
                      Image is the container image to use for backup operations.
                      If not specified, defaults to "<repo>:X.Y.Z" where <repo> is derived from OPERATOR_BACKUP_IMAGE_REPOSITORY
                      (default: "ghcr.io/dc-tec/openbao-backup") and the tag matches OPERATOR_VERSION.
                      This allows users to override the image for air-gapped environments or custom registries.
                    type: string
                  jwtAuthRole:
                    description: |-
                      JWTAuthRole is the name of the JWT Auth role configured in OpenBao
                      for backup operations. When set, the backup executor will use JWT Auth
                      (projected ServiceAccount token) instead of a static token. This is the preferred authentication
                      method as tokens are automatically rotated by Kubernetes.

                      The role must be configured in OpenBao and must grant the "read" capability on
                      sys/storage/raft/snapshot. The role must bind to the backup ServiceAccount
                      (<cluster-name>-backup-serviceaccount) in the cluster namespace.

                      If OIDC is enabled in SelfInit and this field is empty, a default role
                      named "openbao-operator-backup" will be assumed/created.
                    type: string
                  retention:
                    description: Retention defines optional backup retention policy.
                    properties:
                      maxAge:
                        description: |-
                          MaxAge is the maximum age of backups to retain, e.g., "168h" for 7 days.
                          Backups older than this are deleted after successful new backup upload.
                        type: string
                      maxCount:
                        description: MaxCount is the maximum number of backups to
                          retain (0 = unlimited).
                        format: int32
                        minimum: 0
                        type: integer
                    type: object
                  schedule:
                    description: Schedule is a cron-style schedule, for example "0
                      3 * * *".
                    minLength: 1
                    type: string
                  target:
                    description: Target is the object storage configuration for backups.
                    properties:
                      azure:
                        description: |-
                          Azure contains Azure Blob Storage specific configuration.
                          Only used when Provider is "azure".
                        properties:
                          container:
                            description: Container is the blob container name. If
                              empty, uses the Bucket field value.
                            type: string
                          storageAccount:
                            description: |-
                              StorageAccount is the Azure storage account name.
                              Required when using Azure provider.
                            minLength: 1
                            type: string
                        type: object
                      bucket:
                        description: Bucket is the bucket or container name.
                        minLength: 1
                        type: string
                      concurrency:
                        default: 3
                        description: |-
                          Concurrency is the number of concurrent parts to upload during multipart uploads.
                          Defaults to 3. Higher values may improve throughput on fast networks but increase
                          memory usage and may overwhelm slower storage backends.
                        format: int32
                        maximum: 10
                        minimum: 1
                        type: integer
                      credentialsSecretRef:
                        description: |-
                          CredentialsSecretRef optionally references a Secret containing credentials for the object store.
                          The Secret must exist in the same namespace as the owning OpenBao resource.
                          Cross-namespace references are not allowed for security reasons.

                          For S3: Expected keys are "accessKeyId" and "secretAccessKey" (optional: "sessionToken", "region", "caCert").
                          For GCS: Expected key is "credentials.json" containing a service account JSON key.
                          For Azure: Expected keys are "accountKey" or "connectionString".
                          Omit this field when relying on ambient workload identity or another default credential chain.
                        properties:
                          name:
                            default: ""
                            description: |-
                              Name of the referent.
                              This field is effectively required, but due to backwards compatibility is
                              allowed to be empty. Instances of this type with an empty value here are
                              almost certainly wrong.
                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                            type: string
                        type: object
                        x-kubernetes-map-type: atomic
                      endpoint:
                        description: |-
                          Endpoint is the HTTP(S) endpoint for the object storage service.
                          For S3: Required (e.g., "https://s3.amazonaws.com" or MinIO endpoint).
                          For GCS: Optional (defaults to googleapis.com).
                          For Azure: Optional (derived from StorageAccount if not specified).
                        type: string
                      gcs:
                        description: |-
                          GCS contains Google Cloud Storage specific configuration.
                          Only used when Provider is "gcs".
                        properties:
                          project:
                            description: |-
                              Project is the GCP project ID. Optional if using ADC with default project or
                              if the credentials JSON includes the project.
                            type: string
                        type: object
                      insecureSkipVerify:
                        description: |-
                          InsecureSkipVerify allows skipping TLS verification (useful for MinIO/LocalStack/Azurite with self-signed certs).
                          This applies to all providers that support TLS.
                        type: boolean
                      partSize:
                        default: 10485760
                        description: |-
                          PartSize is the size of each part in multipart uploads (in bytes).
                          Defaults to 10MB (10485760 bytes). Larger values may improve performance for large snapshots
                          on fast networks, while smaller values may be better for slow or unreliable networks.
                        format: int64
                        minimum: 5242880
                        type: integer
                      pathPrefix:
                        description: PathPrefix is an optional prefix within the bucket
                          for this cluster's snapshots.
                        type: string
                      provider:
                        default: s3
                        description: Provider selects the storage backend. Defaults
                          to "s3" for backward compatibility.
                        enum:
                        - s3
                        - gcs
                        - azure
                        type: string
                      region:
                        default: us-east-1
                        description: |-
                          Region is the AWS region to use for S3-compatible clients.
                          For AWS, this should match the bucket region (for example, "eu-west-1").
                          For many S3-compatible stores (MinIO/Ceph), this can be any non-empty value.
                          Only used when Provider is "s3".
                        type: string
                      roleArn:
                        description: |-
                          RoleARN is the IAM role ARN (or S3-compatible equivalent) to assume via Web Identity.
                          When set, backup and restore Jobs mount a projected ServiceAccount token and set the
                          AWS Web Identity environment variables explicitly.
                          Leave this empty when relying on ambient workload identity or provider-managed default credentials instead.
                          Only used when Provider is "s3".
                        type: string
                      usePathStyle:
                        default: false
                        description: |-
                          UsePathStyle controls whether to use path-style addressing (bucket.s3.amazonaws.com/object)
                          or virtual-hosted-style addressing (bucket.s3.amazonaws.com/object).
                          Set to true for MinIO and S3-compatible stores that require path-style.
                          Set to false for AWS S3 (default, as AWS is deprecating path-style).
                          Only used when Provider is "s3".
                        type: boolean
                      workloadIdentity:
                        description: |-
                          WorkloadIdentity optionally applies provider-specific metadata required by cloud workload identity integrations.
                          Use this for ambient identity setups such as EKS Pod Identity or IRSA, GKE Workload Identity, or Azure Workload Identity.
                          When omitted, backup and restore workloads can still use any credentials exposed through the pod's default provider chain.
                        properties:
                          podLabels:
                            additionalProperties:
                              type: string
                            description: |-
                              PodLabels are merged into the generated backup or restore Job pod template.
                              This is typically used for provider-specific selectors such as Azure Workload Identity.
                              Operator-managed labels take precedence if the same key is specified here.
                            type: object
                          serviceAccountAnnotations:
                            additionalProperties:
                              type: string
                            description: |-
                              ServiceAccountAnnotations are merged into the generated backup or restore ServiceAccount.
                              This is typically used for provider-specific bindings such as GKE Workload Identity
                              or webhook-based AWS/Azure workload identity integrations.
                            type: object
                        type: object
                    required:
                    - bucket
                    type: object
                  tokenSecretRef:
                    description: |-
                      TokenSecretRef optionally references a Secret containing an OpenBao API
                      token to use for backup operations (fallback method).

                      The Secret must exist in the same namespace as the OpenBaoCluster.
                      Cross-namespace references are not allowed for security reasons.

                      For standard clusters (non self-init), this is typically omitted and the
                      operator uses the root token from <cluster>-root-token. For self-init
                      clusters (no root token Secret), this field must reference a token with
                      permission to read sys/storage/raft/snapshot.

                      If JWTAuthRole is set, this field is ignored in favor of JWT Auth.
                    properties:
                      name:
                        default: ""
                        description: |-
                          Name of the referent.
                          This field is effectively required, but due to backwards compatibility is
                          allowed to be empty. Instances of this type with an empty value here are
                          almost certainly wrong.
                          More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                        type: string
                    type: object
                    x-kubernetes-map-type: atomic
                required:
                - schedule
                - target
                type: object
              breakGlassAck:
                description: |-
                  BreakGlassAck is an explicit acknowledgment token used to exit Break Glass / Safe Mode.

                  When the operator enters break glass mode, it writes a nonce to status.breakGlass.nonce.
                  To acknowledge and allow the operator to resume quorum-risk automation, set this field
                  to match that nonce.

                  Example:
                    kubectl -n <ns> patch openbaocluster <name> --type merge -p '{"spec":{"breakGlassAck":"<nonce>"}}'
                type: string
              configuration:
                description: Configuration defines the server configuration.
                properties:
                  acmeCARoot:
                    description: |-
                      ACMECARoot is the path to the ACME CA root certificate file.
                      This is used when TLS mode is ACME to specify a custom CA root for ACME certificate validation.
                    type: string
                  allowAuditLogPrefixing:
                    description: |-
                      AllowAuditLogPrefixing allows audit log prefixing.
                      This enables custom prefixes in audit log entries.
                    type: boolean
                  cacheSize:
                    description: |-
                      CacheSize is the size of the cache in bytes.
                      If not specified, OpenBao uses its default cache size.
                    format: int64
                    minimum: 0
                    type: integer
                  defaultLeaseTTL:
                    description: |-
                      DefaultLeaseTTL is the default lease TTL for tokens and secrets (e.g., "720h", "30m").
                      If not specified, OpenBao uses its default.
                    type: string
                  detectDeadlocks:
                    description: |-
                      DetectDeadlocks enables deadlock detection in OpenBao.
                      This is an experimental feature for debugging.
                    type: boolean
                  disableCache:
                    description: |-
                      DisableCache disables the cache entirely.
                      When true, all caching is disabled.
                    type: boolean
                  enableResponseHeaderHostname:
                    description: |-
                      EnableResponseHeaderHostname enables the hostname in response headers.
                      When true, OpenBao includes the hostname in HTTP response headers.
                    type: boolean
                  enableResponseHeaderRaftNodeID:
                    description: |-
                      EnableResponseHeaderRaftNodeID enables the Raft node ID in response headers.
                      When true, OpenBao includes the Raft node ID in HTTP response headers.
                    type: boolean
                  impreciseLeaseRoleTracking:
                    description: |-
                      ImpreciseLeaseRoleTracking enables imprecise lease role tracking.
                      This is an experimental feature that may improve performance in some scenarios.
                    type: boolean
                  introspectionEndpoint:
                    description: |-
                      IntrospectionEndpoint enables the introspection endpoint.
                      This is an experimental feature for debugging and introspection.
                    type: boolean
                  listener:
                    description: |-
                      Listener allows tuning the TCP listener.
                      Note: Address and ClusterAddress are managed by the operator and cannot be changed.
                    properties:
                      proxyProtocolBehavior:
                        description: ProxyProtocolBehavior allows configuring proxy
                          protocol (e.g. for LoadBalancers).
                        enum:
                        - use_always
                        - allow_any
                        - deny_unauthorized
                        type: string
                      tlsDisable:
                        description: |-
                          TLSDisable controls TLS on the listener.
                          Note: This is typically managed by the operator based on spec.tls.enabled.
                        type: boolean
                    type: object
                  logLevel:
                    default: info
                    description: LogLevel specifies the log level.
                    enum:
                    - trace
                    - debug
                    - info
                    - warn
                    - err
                    type: string
                  logging:
                    description: Logging allows configuring logging behavior.
                    properties:
                      file:
                        description: |-
                          File is the path to the log file.
                          If not specified, logs are written to stderr.
                        type: string
                      format:
                        description: Format specifies the log format.
                        enum:
                        - standard
                        - json
                        type: string
                      pidFile:
                        description: PIDFile is the path to write the PID file.
                        type: string
                      rotateBytes:
                        description: RotateBytes specifies the maximum size in bytes
                          before rotating logs.
                        format: int64
                        minimum: 0
                        type: integer
                      rotateDuration:
                        description: RotateDuration specifies how often to rotate
                          logs (e.g., "24h", "7d").
                        type: string
                      rotateMaxFiles:
                        description: RotateMaxFiles is the maximum number of rotated
                          log files to keep.
                        format: int32
                        minimum: 0
                        type: integer
                    type: object
                  maxLeaseTTL:
                    description: |-
                      MaxLeaseTTL is the maximum lease TTL for tokens and secrets (e.g., "8760h", "1y").
                      This must be greater than or equal to DefaultLeaseTTL.
                      If not specified, OpenBao uses its default.
                    type: string
                  plugin:
                    description: |-
                      Plugin allows configuring plugin behavior.
                      Note: This is separate from spec.plugins which defines plugin instances.
                    properties:
                      autoDownload:
                        description: AutoDownload controls automatic plugin downloads
                          from OCI registries.
                        type: boolean
                      autoRegister:
                        description: AutoRegister controls automatic plugin registration.
                        type: boolean
                      downloadBehavior:
                        description: DownloadBehavior specifies how plugins are downloaded.
                        enum:
                        - standard
                        - direct
                        type: string
                      filePermissions:
                        description: FilePermissions are the file permissions for
                          plugin files (e.g., "0755").
                        type: string
                      fileUID:
                        description: FileUID is the UID to use for plugin files.
                        format: int64
                        type: integer
                    type: object
                  raft:
                    description: Raft allows tuning the Raft storage backend.
                    properties:
                      autopilot:
                        description: |-
                          Autopilot configures Raft Autopilot settings.
                          By default, dead server cleanup is enabled with a 5-minute threshold.
                        properties:
                          cleanupDeadServers:
                            default: true
                            description: |-
                              CleanupDeadServers enables automatic removal of dead Raft peers.
                              When enabled, Autopilot periodically removes servers that have been
                              unhealthy for longer than DeadServerLastContactThreshold.
                              Requires MinQuorum to be set (defaults to replicas/2 + 1).
                            type: boolean
                          deadServerLastContactThreshold:
                            default: 5m
                            description: |-
                              DeadServerLastContactThreshold is the duration after which a server
                              is considered dead if it hasn't contacted the leader.
                              Minimum: "1m". Default: "5m" (operator default, shorter than OpenBao's 24h).
                            type: string
                          lastContactThreshold:
                            description: |-
                              LastContactThreshold is the limit on the amount of time a server can
                              go without leader contact before being considered unhealthy.
                              Default: "10s".
                            type: string
                          maxTrailingLogs:
                            description: |-
                              MaxTrailingLogs is the amount of entries in the Raft Log that a server
                              can be behind before being considered unhealthy. Default: 1000.
                            format: int32
                            type: integer
                          minQuorum:
                            description: |-
                              MinQuorum is the minimum number of servers before Autopilot can prune
                              dead servers. This prevents removing so many servers that quorum is lost.
                              If not specified, defaults to max(3, replicas/2 + 1).
                            format: int32
                            minimum: 3
                            type: integer
                          serverStabilizationTime:
                            description: |-
                              ServerStabilizationTime is the minimum time a server must be healthy
                              before being promoted to voter. Default: "10s".
                            type: string
                        type: object
                      performanceMultiplier:
                        description: PerformanceMultiplier scales the Raft timing
                          parameters.
                        format: int32
                        minimum: 0
                        type: integer
                    type: object
                  rawStorageEndpoint:
                    description: |-
                      RawStorageEndpoint enables the raw storage endpoint.
                      This is an experimental feature that exposes raw storage operations.
                    type: boolean
                  ui:
                    default: true
                    description: UI enables the built-in web interface.
                    type: boolean
                  unsafeAllowAPIAuditCreation:
                    description: |-
                      UnsafeAllowAPIAuditCreation allows API-based audit device creation.
                      This bypasses the normal audit device configuration validation.
                      Use with caution.
                    type: boolean
                type: object
              deletionPolicy:
                description: DeletionPolicy controls what happens to underlying resources
                  when the CR is deleted.
                enum:
                - Retain
                - DeletePVCs
                - DeleteAll
                type: string
              gateway:
                description: |-
                  Gateway configures Kubernetes Gateway API access (alternative to Ingress).
                  When enabled, the Operator creates an HTTPRoute that routes traffic through
                  a user-managed Gateway resource.
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    description: Annotations to apply to the HTTPRoute resource.
                    type: object
                  backendTLS:
                    description: |-
                      BackendTLS configures BackendTLSPolicy for end-to-end TLS between the Gateway and OpenBao.
                      When enabled, the Operator creates a BackendTLSPolicy that configures the Gateway to use
                      HTTPS when communicating with the OpenBao backend service and validates the backend
                      certificate using the cluster's CA certificate.
                    properties:
                      enabled:
                        default: true
                        description: |-
                          Enabled controls whether the Operator creates a BackendTLSPolicy.
                          When true (default when Gateway is enabled), the Operator creates a BackendTLSPolicy
                          that enables HTTPS and certificate validation for backend connections.
                          When false, no BackendTLSPolicy is created and the Gateway will use HTTP (or rely on
                          external configuration for TLS).
                        type: boolean
                      hostname:
                        description: |-
                          Hostname is the hostname to verify in the backend certificate.
                          If not specified, defaults to the Service DNS name: <service-name>.<namespace>.svc
                          This should match the certificate SAN or the service DNS name.
                        type: string
                    type: object
                  enabled:
                    description: |-
                      Enabled activates Gateway API support for this cluster.
                      When true, the Operator creates an HTTPRoute for the cluster.
                    type: boolean
                  gatewayRef:
                    description: |-
                      GatewayRef references an existing Gateway resource that will handle
                      traffic for this OpenBao cluster. The Gateway must already exist.
                    properties:
                      name:
                        description: Name of the Gateway resource.
                        minLength: 1
                        type: string
                      namespace:
                        description: Namespace of the Gateway resource. If empty,
                          uses the OpenBaoCluster namespace.
                        type: string
                    required:
                    - name
                    type: object
                  hostname:
                    description: |-
                      Hostname for routing traffic to this OpenBao cluster.
                      This hostname will be automatically added to the TLS SANs.
                    minLength: 1
                    type: string
                  listenerName:
                    description: |-
                      ListenerName optionally targets a specific listener (sectionName) on the referenced Gateway.
                      When set, the generated Route (HTTPRoute or TLSRoute) attaches only to that listener.
                      This is useful when a Gateway exposes multiple listeners for the same hostname (e.g. Traefik
                      "web" and "websecure") and you want deterministic attachment.
                    type: string
                  path:
                    description: Path prefix for the HTTPRoute (defaults to "/").
                    type: string
                  tlsPassthrough:
                    description: |-
                      TLSPassthrough enables TLS passthrough mode using TLSRoute instead of HTTPRoute.
                      When true, the Operator creates a TLSRoute that routes encrypted TLS traffic based on SNI
                      without terminating TLS at the Gateway. OpenBao terminates TLS directly.
                      When false (default), the Operator creates an HTTPRoute with TLS termination at the Gateway.
                      Note: TLSRoute and HTTPRoute are mutually exclusive - only one can be used per cluster.
                      BackendTLSPolicy is not needed when TLSPassthrough is enabled since the Gateway does not
                      decrypt traffic. The Gateway listener must be configured with protocol: TLS and mode: Passthrough.
                    type: boolean
                required:
                - enabled
                - gatewayRef
                - hostname
                type: object
              image:
                description: Image is the container image to run; defaults may be
                  derived from Version.
                type: string
              imagePullSecrets:
                description: |-
                  ImagePullSecrets is a list of references to secrets in the same namespace
                  to use for pulling any images used by this Cluster (server, init, sidecars).
                items:
                  description: |-
                    LocalObjectReference contains enough information to let you locate the
                    referenced object inside the same namespace.
                  properties:
                    name:
                      default: ""
                      description: |-
                        Name of the referent.
                        This field is effectively required, but due to backwards compatibility is
                        allowed to be empty. Instances of this type with an empty value here are
                        almost certainly wrong.
                        More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                      type: string
                  type: object
                  x-kubernetes-map-type: atomic
                type: array
              imageVerification:
                description: ImageVerification configures supply chain security checks.
                properties:
                  enabled:
                    description: Enabled controls whether image verification is enforced.
                    type: boolean
                  failurePolicy:
                    default: Block
                    description: |-
                      FailurePolicy defines behavior on verification failure.
                      "Block" blocks reconciliation of the affected workload when verification fails.
                      "Warn" logs an error and emits a Kubernetes Event but proceeds.
                    enum:
                    - Warn
                    - Block
                    type: string
                  ignoreTlog:
                    default: false
                    description: |-
                      IgnoreTlog controls whether to verify against the Rekor transparency log.
                      When false (default), signatures are verified against Rekor for non-repudiation.
                      When true, only signature verification is performed without transparency log checks.
                    type: boolean
                  imagePullSecrets:
                    description: |-
                      ImagePullSecrets is a list of references to secrets in the same namespace
                      to use for pulling images from private registries during verification.
                      These secrets must be of type kubernetes.io/dockerconfigjson or kubernetes.io/dockercfg.
                    items:
                      description: |-
                        LocalObjectReference contains enough information to let you locate the
                        referenced object inside the same namespace.
                      properties:
                        name:
                          default: ""
                          description: |-
                            Name of the referent.
                            This field is effectively required, but due to backwards compatibility is
                            allowed to be empty. Instances of this type with an empty value here are
                            almost certainly wrong.
                            More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                          type: string
                      type: object
                      x-kubernetes-map-type: atomic
                    type: array
                  issuer:
                    description: |-
                      Issuer is the OIDC issuer for keyless verification (e.g., https://token.actions.githubusercontent.com).
                      Required for keyless verification when PublicKey is not provided.
                      For GitHub Actions keyless verification, use: https://token.actions.githubusercontent.com
                    type: string
                  issuerRegExp:
                    description: |-
                      IssuerRegExp is a regular expression for the OIDC issuer when using keyless verification.
                      Use this to allow a controlled set of issuers instead of a single exact issuer string.
                      Requires SubjectRegExp when PublicKey is not provided.
                    type: string
                  publicKey:
                    description: |-
                      PublicKey is the Cosign public key content used to verify the signature.
                      Required for static key verification. If empty, keyless verification will be used
                      (requires Issuer and Subject to be set).
                    type: string
                  subject:
                    description: |-
                      Subject is the OIDC subject for keyless verification.
                      Required for keyless verification when PublicKey is not provided.
                      Example (GitHub Actions): https://github.com/dc-tec/openbao-operator/.github/workflows/release.yml@refs/tags/<VERSION>
                      The version in the subject MUST match the image tag version.
                    type: string
                  subjectRegExp:
                    description: |-
                      SubjectRegExp is a regular expression for the OIDC subject when using keyless verification.
                      Use this to allow a controlled set of workflow identities instead of a single exact subject.
                      Requires IssuerRegExp when PublicKey is not provided.
                    type: string
                required:
                - enabled
                - failurePolicy
                type: object
              ingress:
                description: Ingress configures optional HTTP(S) ingress in front
                  of the OpenBao Service.
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    description: Annotations are additional annotations to apply to
                      the Ingress.
                    type: object
                  className:
                    description: ClassName is an optional IngressClassName (for example,
                      "nginx", "traefik").
                    type: string
                  enabled:
                    description: Enabled controls whether the Operator manages an
                      Ingress for external access.
                    type: boolean
                  host:
                    description: Host is the primary host for external access, for
                      example "bao.example.com".
                    minLength: 1
                    type: string
                  path:
                    description: Path is the HTTP path to route to OpenBao, defaulting
                      to "/".
                    type: string
                  tlsSecretName:
                    description: TLSSecretName is an optional TLS Secret name; when
                      empty the cluster TLS Secret is used.
                    type: string
                required:
                - host
                type: object
              initContainer:
                description: |-
                  InitContainer configures the init container used to render OpenBao configuration.
                  The init container renders the final config.hcl from a template using environment
                  variables such as HOSTNAME and POD_IP.
                properties:
                  enabled:
                    default: true
                    description: |-
                      Enabled controls whether the init container is used to render the configuration.
                      The operator requires the init container; disabling it is not supported.
                    type: boolean
                  image:
                    description: |-
                      Image is the container image to use for the init container.
                      If not specified, defaults to "<repo>:X.Y.Z" where <repo> is derived from OPERATOR_INIT_IMAGE_REPOSITORY
                      (default: "ghcr.io/dc-tec/openbao-init") and the tag matches OPERATOR_VERSION.
                    type: string
                type: object
              maintenance:
                description: Maintenance configures supported maintenance and restart
                  workflows.
                properties:
                  enabled:
                    description: |-
                      Enabled enables maintenance mode for this cluster.
                      When true, the operator annotates managed resources (Pods/StatefulSet) with
                      `openbao.org/maintenance=true` to allow controlled restarts/deletes where
                      admission policies require an explicit maintenance signal.
                    type: boolean
                  restartAt:
                    description: |-
                      RestartAt triggers a rolling restart when changed.
                      The operator propagates this value as a Pod template annotation; any change
                      results in a new StatefulSet revision and a controlled restart.
                      Recommended value is an RFC3339 timestamp string.
                    minLength: 1
                    type: string
                type: object
              network:
                description: Network configures network-related settings for the cluster.
                properties:
                  apiServerCIDR:
                    description: |-
                      APIServerCIDR is an optional CIDR block for the Kubernetes API server.
                      When specified, this value is used instead of auto-detection for NetworkPolicy egress rules.
                      This is useful when you want an explicit allow-list (or when the in-cluster service VIP
                      injected into pods is unavailable/unusable in your environment).
                      Example: "10.43.0.0/16" for service network or "192.168.1.0/24" for control plane nodes.
                    type: string
                  apiServerEndpointIPs:
                    description: |-
                      APIServerEndpointIPs is an optional list of Kubernetes API server endpoint IPs.
                      When set, the operator adds least-privilege NetworkPolicy egress rules for these IPs on port 6443.
                      This is required on some CNI implementations where egress enforcement happens on the post-NAT
                      destination (the API server endpoint) rather than the kubernetes Service IP (10.43.0.1:443).

                      The operator does not auto-detect these endpoint IPs because doing so reliably requires broader
                      cluster permissions (list/watch). Configure this field explicitly when needed.
                      Example (k3d): ["192.168.166.2"]
                    items:
                      type: string
                    type: array
                  dnsEndpointIPs:
                    description: |-
                      DNSEndpointIPs is an optional list of DNS resolver endpoint IPs that should be
                      allow-listed directly in the operator-managed NetworkPolicy on TCP/UDP port 53.

                      Use this for clusters that resolve DNS through node-local or host-networked caches
                      instead of pod-backed DNS Services in a namespace. These IP-based rules are additive
                      to the namespace-based allow-list controlled by DNSNamespace.

                      The operator does not auto-detect these endpoint IPs because doing so reliably would
                      require environment-specific node or DNS discovery logic outside the current trust model.
                      Example: ["169.254.20.10"]
                    items:
                      type: string
                    type: array
                  dnsNamespace:
                    default: kube-system
                    description: |-
                      DNSNamespace specifies the namespace where the cluster DNS service resides.
                      Defaults to "kube-system" if not specified.
                    type: string
                  egressRules:
                    description: |-
                      EgressRules allows users to specify additional egress rules that will be merged into
                      the operator-managed NetworkPolicy. This is useful for allowing access to external
                      services such as transit seal backends, object storage endpoints, or other dependencies.

                      The operator's default egress rules (DNS, API server, cluster pods) are always included
                      and cannot be overridden. User-provided rules are appended to the operator-managed rules.

                      Example: Allow egress to a transit seal backend in another namespace:
                        egressRules:
                        - to:
                          - namespaceSelector:
                              matchLabels:
                                kubernetes.io/metadata.name: transit-namespace
                          ports:
                          - protocol: TCP
                            port: 8200
                    items:
                      description: |-
                        NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods
                        matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to.
                        This type is beta-level in 1.8
                      properties:
                        ports:
                          description: |-
                            ports is a list of destination ports for outgoing traffic.
                            Each item in this list is combined using a logical OR. If this field is
                            empty or missing, this rule matches all ports (traffic not restricted by port).
                            If this field is present and contains at least one item, then this rule allows
                            traffic only if the traffic matches at least one port in the list.
                          items:
                            description: NetworkPolicyPort describes a port to allow
                              traffic on
                            properties:
                              endPort:
                                description: |-
                                  endPort indicates that the range of ports from port to endPort if set, inclusive,
                                  should be allowed by the policy. This field cannot be defined if the port field
                                  is not defined or if the port field is defined as a named (string) port.
                                  The endPort must be equal or greater than port.
                                format: int32
                                type: integer
                              port:
                                anyOf:
                                - type: integer
                                - type: string
                                description: |-
                                  port represents the port on the given protocol. This can either be a numerical or named
                                  port on a pod. If this field is not provided, this matches all port names and
                                  numbers.
                                  If present, only traffic on the specified protocol AND port will be matched.
                                x-kubernetes-int-or-string: true
                              protocol:
                                description: |-
                                  protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match.
                                  If not specified, this field defaults to TCP.
                                type: string
                            type: object
                          type: array
                          x-kubernetes-list-type: atomic
                        to:
                          description: |-
                            to is a list of destinations for outgoing traffic of pods selected for this rule.
                            Items in this list are combined using a logical OR operation. If this field is
                            empty or missing, this rule matches all destinations (traffic not restricted by
                            destination). If this field is present and contains at least one item, this rule
                            allows traffic only if the traffic matches at least one item in the to list.
                          items:
                            description: |-
                              NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of
                              fields are allowed
                            properties:
                              ipBlock:
                                description: |-
                                  ipBlock defines policy on a particular IPBlock. If this field is set then
                                  neither of the other fields can be.
                                properties:
                                  cidr:
                                    description: |-
                                      cidr is a string representing the IPBlock
                                      Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                                    type: string
                                  except:
                                    description: |-
                                      except is a slice of CIDRs that should not be included within an IPBlock
                                      Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                                      Except values will be rejected if they are outside the cidr range
                                    items:
                                      type: string
                                    type: array
                                    x-kubernetes-list-type: atomic
                                required:
                                - cidr
                                type: object
                              namespaceSelector:
                                description: |-
                                  namespaceSelector selects namespaces using cluster-scoped labels. This field follows
                                  standard label selector semantics; if present but empty, it selects all namespaces.

                                  If podSelector is also set, then the NetworkPolicyPeer as a whole selects
                                  the pods matching podSelector in the namespaces selected by namespaceSelector.
                                  Otherwise it selects all pods in the namespaces selected by namespaceSelector.
                                properties:
                                  matchExpressions:
                                    description: matchExpressions is a list of label
                                      selector requirements. The requirements are
                                      ANDed.
                                    items:
                                      description: |-
                                        A label selector requirement is a selector that contains values, a key, and an operator that
                                        relates the key and values.
                                      properties:
                                        key:
                                          description: key is the label key that the
                                            selector applies to.
                                          type: string
                                        operator:
                                          description: |-
                                            operator represents a key's relationship to a set of values.
                                            Valid operators are In, NotIn, Exists and DoesNotExist.
                                          type: string
                                        values:
                                          description: |-
                                            values is an array of string values. If the operator is In or NotIn,
                                            the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                            the values array must be empty. This array is replaced during a strategic
                                            merge patch.
                                          items:
                                            type: string
                                          type: array
                                          x-kubernetes-list-type: atomic
                                      required:
                                      - key
                                      - operator
                                      type: object
                                    type: array
                                    x-kubernetes-list-type: atomic
                                  matchLabels:
                                    additionalProperties:
                                      type: string
                                    description: |-
                                      matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                      map is equivalent to an element of matchExpressions, whose key field is "key", the
                                      operator is "In", and the values array contains only "value". The requirements are ANDed.
                                    type: object
                                type: object
                                x-kubernetes-map-type: atomic
                              podSelector:
                                description: |-
                                  podSelector is a label selector which selects pods. This field follows standard label
                                  selector semantics; if present but empty, it selects all pods.

                                  If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects
                                  the pods matching podSelector in the Namespaces selected by NamespaceSelector.
                                  Otherwise it selects the pods matching podSelector in the policy's own namespace.
                                properties:
                                  matchExpressions:
                                    description: matchExpressions is a list of label
                                      selector requirements. The requirements are
                                      ANDed.
                                    items:
                                      description: |-
                                        A label selector requirement is a selector that contains values, a key, and an operator that
                                        relates the key and values.
                                      properties:
                                        key:
                                          description: key is the label key that the
                                            selector applies to.
                                          type: string
                                        operator:
                                          description: |-
                                            operator represents a key's relationship to a set of values.
                                            Valid operators are In, NotIn, Exists and DoesNotExist.
                                          type: string
                                        values:
                                          description: |-
                                            values is an array of string values. If the operator is In or NotIn,
                                            the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                            the values array must be empty. This array is replaced during a strategic
                                            merge patch.
                                          items:
                                            type: string
                                          type: array
                                          x-kubernetes-list-type: atomic
                                      required:
                                      - key
                                      - operator
                                      type: object
                                    type: array
                                    x-kubernetes-list-type: atomic
                                  matchLabels:
                                    additionalProperties:
                                      type: string
                                    description: |-
                                      matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                      map is equivalent to an element of matchExpressions, whose key field is "key", the
                                      operator is "In", and the values array contains only "value". The requirements are ANDed.
                                    type: object
                                type: object
                                x-kubernetes-map-type: atomic
                            type: object
                          type: array
                          x-kubernetes-list-type: atomic
                      type: object
                    type: array
                  ingressRules:
                    description: |-
                      IngressRules allows users to specify additional ingress rules that will be merged into
                      the operator-managed NetworkPolicy. This is useful for allowing access from external
                      services, monitoring tools, or other components that need to reach OpenBao pods.

                      The operator's default ingress rules (cluster pods, kube-system, operator, gateway)
                      are always included and cannot be overridden. User-provided rules are appended to
                      the operator-managed rules.

                      Example: Allow ingress from a monitoring namespace:
                        ingressRules:
                        - from:
                          - namespaceSelector:
                              matchLabels:
                                kubernetes.io/metadata.name: monitoring
                          ports:
                          - protocol: TCP
                            port: 8200
                    items:
                      description: |-
                        NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods
                        matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.
                      properties:
                        from:
                          description: |-
                            from is a list of sources which should be able to access the pods selected for this rule.
                            Items in this list are combined using a logical OR operation. If this field is
                            empty or missing, this rule matches all sources (traffic not restricted by
                            source). If this field is present and contains at least one item, this rule
                            allows traffic only if the traffic matches at least one item in the from list.
                          items:
                            description: |-
                              NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of
                              fields are allowed
                            properties:
                              ipBlock:
                                description: |-
                                  ipBlock defines policy on a particular IPBlock. If this field is set then
                                  neither of the other fields can be.
                                properties:
                                  cidr:
                                    description: |-
                                      cidr is a string representing the IPBlock
                                      Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                                    type: string
                                  except:
                                    description: |-
                                      except is a slice of CIDRs that should not be included within an IPBlock
                                      Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                                      Except values will be rejected if they are outside the cidr range
                                    items:
                                      type: string
                                    type: array
                                    x-kubernetes-list-type: atomic
                                required:
                                - cidr
                                type: object
                              namespaceSelector:
                                description: |-
                                  namespaceSelector selects namespaces using cluster-scoped labels. This field follows
                                  standard label selector semantics; if present but empty, it selects all namespaces.

                                  If podSelector is also set, then the NetworkPolicyPeer as a whole selects
                                  the pods matching podSelector in the namespaces selected by namespaceSelector.
                                  Otherwise it selects all pods in the namespaces selected by namespaceSelector.
                                properties:
                                  matchExpressions:
                                    description: matchExpressions is a list of label
                                      selector requirements. The requirements are
                                      ANDed.
                                    items:
                                      description: |-
                                        A label selector requirement is a selector that contains values, a key, and an operator that
                                        relates the key and values.
                                      properties:
                                        key:
                                          description: key is the label key that the
                                            selector applies to.
                                          type: string
                                        operator:
                                          description: |-
                                            operator represents a key's relationship to a set of values.
                                            Valid operators are In, NotIn, Exists and DoesNotExist.
                                          type: string
                                        values:
                                          description: |-
                                            values is an array of string values. If the operator is In or NotIn,
                                            the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                            the values array must be empty. This array is replaced during a strategic
                                            merge patch.
                                          items:
                                            type: string
                                          type: array
                                          x-kubernetes-list-type: atomic
                                      required:
                                      - key
                                      - operator
                                      type: object
                                    type: array
                                    x-kubernetes-list-type: atomic
                                  matchLabels:
                                    additionalProperties:
                                      type: string
                                    description: |-
                                      matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                      map is equivalent to an element of matchExpressions, whose key field is "key", the
                                      operator is "In", and the values array contains only "value". The requirements are ANDed.
                                    type: object
                                type: object
                                x-kubernetes-map-type: atomic
                              podSelector:
                                description: |-
                                  podSelector is a label selector which selects pods. This field follows standard label
                                  selector semantics; if present but empty, it selects all pods.

                                  If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects
                                  the pods matching podSelector in the Namespaces selected by NamespaceSelector.
                                  Otherwise it selects the pods matching podSelector in the policy's own namespace.
                                properties:
                                  matchExpressions:
                                    description: matchExpressions is a list of label
                                      selector requirements. The requirements are
                                      ANDed.
                                    items:
                                      description: |-
                                        A label selector requirement is a selector that contains values, a key, and an operator that
                                        relates the key and values.
                                      properties:
                                        key:
                                          description: key is the label key that the
                                            selector applies to.
                                          type: string
                                        operator:
                                          description: |-
                                            operator represents a key's relationship to a set of values.
                                            Valid operators are In, NotIn, Exists and DoesNotExist.
                                          type: string
                                        values:
                                          description: |-
                                            values is an array of string values. If the operator is In or NotIn,
                                            the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                            the values array must be empty. This array is replaced during a strategic
                                            merge patch.
                                          items:
                                            type: string
                                          type: array
                                          x-kubernetes-list-type: atomic
                                      required:
                                      - key
                                      - operator
                                      type: object
                                    type: array
                                    x-kubernetes-list-type: atomic
                                  matchLabels:
                                    additionalProperties:
                                      type: string
                                    description: |-
                                      matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                      map is equivalent to an element of matchExpressions, whose key field is "key", the
                                      operator is "In", and the values array contains only "value". The requirements are ANDed.
                                    type: object
                                type: object
                                x-kubernetes-map-type: atomic
                            type: object
                          type: array
                          x-kubernetes-list-type: atomic
                        ports:
                          description: |-
                            ports is a list of ports which should be made accessible on the pods selected for
                            this rule. Each item in this list is combined using a logical OR. If this field is
                            empty or missing, this rule matches all ports (traffic not restricted by port).
                            If this field is present and contains at least one item, then this rule allows
                            traffic only if the traffic matches at least one port in the list.
                          items:
                            description: NetworkPolicyPort describes a port to allow
                              traffic on
                            properties:
                              endPort:
                                description: |-
                                  endPort indicates that the range of ports from port to endPort if set, inclusive,
                                  should be allowed by the policy. This field cannot be defined if the port field
                                  is not defined or if the port field is defined as a named (string) port.
                                  The endPort must be equal or greater than port.
                                format: int32
                                type: integer
                              port:
                                anyOf:
                                - type: integer
                                - type: string
                                description: |-
                                  port represents the port on the given protocol. This can either be a numerical or named
                                  port on a pod. If this field is not provided, this matches all port names and
                                  numbers.
                                  If present, only traffic on the specified protocol AND port will be matched.
                                x-kubernetes-int-or-string: true
                              protocol:
                                description: |-
                                  protocol represents the protocol (TCP, UDP, or SCTP) which traffic must match.
                                  If not specified, this field defaults to TCP.
                                type: string
                            type: object
                          type: array
                          x-kubernetes-list-type: atomic
                      type: object
                    type: array
                  trustedIngressPeers:
                    description: |-
                      TrustedIngressPeers allows users to declare ingress-controller or passthrough-proxy peers
                      that should be allowed to reach OpenBao on the API port without writing full raw
                      NetworkPolicy ingress rules.

                      This is useful for user-managed TCP passthrough or external ingress components that the
                      operator does not manage directly. The operator adds least-privilege ingress rules for
                      port 8200 using these peers.

                      Example: Allow a Traefik namespace to reach OpenBao on port 8200:
                        trustedIngressPeers:
                        - namespaceSelector:
                            matchLabels:
                              kubernetes.io/metadata.name: traefik

                      Example: Allow only specific ingress-controller pods in another namespace:
                        trustedIngressPeers:
                        - namespaceSelector:
                            matchLabels:
                              kubernetes.io/metadata.name: ingress-system
                          podSelector:
                            matchLabels:
                              app.kubernetes.io/name: traefik
                    items:
                      description: |-
                        NetworkPolicyPeer describes a peer to allow traffic to/from. Only certain combinations of
                        fields are allowed
                      properties:
                        ipBlock:
                          description: |-
                            ipBlock defines policy on a particular IPBlock. If this field is set then
                            neither of the other fields can be.
                          properties:
                            cidr:
                              description: |-
                                cidr is a string representing the IPBlock
                                Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                              type: string
                            except:
                              description: |-
                                except is a slice of CIDRs that should not be included within an IPBlock
                                Valid examples are "192.168.1.0/24" or "2001:db8::/64"
                                Except values will be rejected if they are outside the cidr range
                              items:
                                type: string
                              type: array
                              x-kubernetes-list-type: atomic
                          required:
                          - cidr
                          type: object
                        namespaceSelector:
                          description: |-
                            namespaceSelector selects namespaces using cluster-scoped labels. This field follows
                            standard label selector semantics; if present but empty, it selects all namespaces.

                            If podSelector is also set, then the NetworkPolicyPeer as a whole selects
                            the pods matching podSelector in the namespaces selected by namespaceSelector.
                            Otherwise it selects all pods in the namespaces selected by namespaceSelector.
                          properties:
                            matchExpressions:
                              description: matchExpressions is a list of label selector
                                requirements. The requirements are ANDed.
                              items:
                                description: |-
                                  A label selector requirement is a selector that contains values, a key, and an operator that
                                  relates the key and values.
                                properties:
                                  key:
                                    description: key is the label key that the selector
                                      applies to.
                                    type: string
                                  operator:
                                    description: |-
                                      operator represents a key's relationship to a set of values.
                                      Valid operators are In, NotIn, Exists and DoesNotExist.
                                    type: string
                                  values:
                                    description: |-
                                      values is an array of string values. If the operator is In or NotIn,
                                      the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                      the values array must be empty. This array is replaced during a strategic
                                      merge patch.
                                    items:
                                      type: string
                                    type: array
                                    x-kubernetes-list-type: atomic
                                required:
                                - key
                                - operator
                                type: object
                              type: array
                              x-kubernetes-list-type: atomic
                            matchLabels:
                              additionalProperties:
                                type: string
                              description: |-
                                matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                map is equivalent to an element of matchExpressions, whose key field is "key", the
                                operator is "In", and the values array contains only "value". The requirements are ANDed.
                              type: object
                          type: object
                          x-kubernetes-map-type: atomic
                        podSelector:
                          description: |-
                            podSelector is a label selector which selects pods. This field follows standard label
                            selector semantics; if present but empty, it selects all pods.

                            If namespaceSelector is also set, then the NetworkPolicyPeer as a whole selects
                            the pods matching podSelector in the Namespaces selected by NamespaceSelector.
                            Otherwise it selects the pods matching podSelector in the policy's own namespace.
                          properties:
                            matchExpressions:
                              description: matchExpressions is a list of label selector
                                requirements. The requirements are ANDed.
                              items:
                                description: |-
                                  A label selector requirement is a selector that contains values, a key, and an operator that
                                  relates the key and values.
                                properties:
                                  key:
                                    description: key is the label key that the selector
                                      applies to.
                                    type: string
                                  operator:
                                    description: |-
                                      operator represents a key's relationship to a set of values.
                                      Valid operators are In, NotIn, Exists and DoesNotExist.
                                    type: string
                                  values:
                                    description: |-
                                      values is an array of string values. If the operator is In or NotIn,
                                      the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                      the values array must be empty. This array is replaced during a strategic
                                      merge patch.
                                    items:
                                      type: string
                                    type: array
                                    x-kubernetes-list-type: atomic
                                required:
                                - key
                                - operator
                                type: object
                              type: array
                              x-kubernetes-list-type: atomic
                            matchLabels:
                              additionalProperties:
                                type: string
                              description: |-
                                matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
                                map is equivalent to an element of matchExpressions, whose key field is "key", the
                                operator is "In", and the values array contains only "value". The requirements are ANDed.
                              type: object
                          type: object
                          x-kubernetes-map-type: atomic
                      type: object
                    type: array
                type: object
              observability:
                description: Observability configures telemetry and metrics integration.
                properties:
                  metrics:
                    description: Metrics configures integration with Prometheus/OpenMetrics.
                    properties:
                      enabled:
                        default: false
                        description: Enabled configures the OpenBao telemetry stanza
                          and creates a ServiceMonitor.
                        type: boolean
                      serviceMonitor:
                        description: ServiceMonitor controls whether to create a Prometheus
                          Operator ServiceMonitor.
                        properties:
                          enabled:
                            default: true
                            description: |-
                              Enabled controls whether to create the ServiceMonitor.
                              Defaults to true if Metrics are enabled.
                            type: boolean
                          interval:
                            default: 30s
                            description: Interval is the scrape interval.
                            type: string
                          scrapeTimeout:
                            default: 10s
                            description: ScrapeTimeout is the scrape timeout.
                            type: string
                        required:
                        - enabled
                        type: object
                    required:
                    - enabled
                    type: object
                type: object
              operatorImageVerification:
                description: |-
                  OperatorImageVerification configures supply chain security checks for operator-managed helper images
                  (init container, backup/upgrade/restore executors). These images are typically signed
                  by the operator project (e.g., dc-tec/openbao-operator) rather than the OpenBao upstream project.
                  If omitted, helper image verification does not fall back to ImageVerification.
                  In Development, omitted means disabled. In Hardened, omitted means enabled.
                properties:
                  enabled:
                    description: Enabled controls whether image verification is enforced.
                    type: boolean
                  failurePolicy:
                    default: Block
                    description: |-
                      FailurePolicy defines behavior on verification failure.
                      "Block" blocks reconciliation of the affected workload when verification fails.
                      "Warn" logs an error and emits a Kubernetes Event but proceeds.
                    enum:
                    - Warn
                    - Block
                    type: string
                  ignoreTlog:
                    default: false
                    description: |-
                      IgnoreTlog controls whether to verify against the Rekor transparency log.
                      When false (default), signatures are verified against Rekor for non-repudiation.
                      When true, only signature verification is performed without transparency log checks.
                    type: boolean
                  imagePullSecrets:
                    description: |-
                      ImagePullSecrets is a list of references to secrets in the same namespace
                      to use for pulling images from private registries during verification.
                      These secrets must be of type kubernetes.io/dockerconfigjson or kubernetes.io/dockercfg.
                    items:
                      description: |-
                        LocalObjectReference contains enough information to let you locate the
                        referenced object inside the same namespace.
                      properties:
                        name:
                          default: ""
                          description: |-
                            Name of the referent.
                            This field is effectively required, but due to backwards compatibility is
                            allowed to be empty. Instances of this type with an empty value here are
                            almost certainly wrong.
                            More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                          type: string
                      type: object
                      x-kubernetes-map-type: atomic
                    type: array
                  issuer:
                    description: |-
                      Issuer is the OIDC issuer for keyless verification (e.g., https://token.actions.githubusercontent.com).
                      Required for keyless verification when PublicKey is not provided.
                      For GitHub Actions keyless verification, use: https://token.actions.githubusercontent.com
                    type: string
                  issuerRegExp:
                    description: |-
                      IssuerRegExp is a regular expression for the OIDC issuer when using keyless verification.
                      Use this to allow a controlled set of issuers instead of a single exact issuer string.
                      Requires SubjectRegExp when PublicKey is not provided.
                    type: string
                  publicKey:
                    description: |-
                      PublicKey is the Cosign public key content used to verify the signature.
                      Required for static key verification. If empty, keyless verification will be used
                      (requires Issuer and Subject to be set).
                    type: string
                  subject:
                    description: |-
                      Subject is the OIDC subject for keyless verification.
                      Required for keyless verification when PublicKey is not provided.
                      Example (GitHub Actions): https://github.com/dc-tec/openbao-operator/.github/workflows/release.yml@refs/tags/<VERSION>
                      The version in the subject MUST match the image tag version.
                    type: string
                  subjectRegExp:
                    description: |-
                      SubjectRegExp is a regular expression for the OIDC subject when using keyless verification.
                      Use this to allow a controlled set of workflow identities instead of a single exact subject.
                      Requires IssuerRegExp when PublicKey is not provided.
                    type: string
                required:
                - enabled
                - failurePolicy
                type: object
              paused:
                description: Paused, when true, pauses reconciliation for this OpenBaoCluster
                  (except delete and finalizers).
                type: boolean
              plugins:
                description: |-
                  Plugins configures declarative plugins for the OpenBao cluster.
                  See: https://openbao.org/docs/configuration/plugins/
                items:
                  description: |-
                    Plugin defines a declarative plugin configuration.
                    See: https://openbao.org/docs/configuration/plugins/
                  properties:
                    args:
                      description: |-
                        Args are arguments to pass to the running plugin.
                        Only used if plugin_auto_register=true is set.
                      items:
                        type: string
                      type: array
                    binaryName:
                      description: BinaryName is the name of the plugin binary file
                        within the OCI image.
                      minLength: 1
                      type: string
                    command:
                      description: |-
                        Command is the command name of a manually downloaded plugin.
                        Required if Image is not set. Conflicts with Image.
                      type: string
                    env:
                      description: |-
                        Env are environment variables to pass to the running plugin.
                        Only used if plugin_auto_register=true is set.
                      items:
                        type: string
                      type: array
                    image:
                      description: |-
                        Image is the OCI image URL including registry and repository.
                        Required if Command is not set. Conflicts with Command.
                      type: string
                    name:
                      description: Name is the name of the plugin.
                      minLength: 1
                      type: string
                    sha256sum:
                      description: |-
                        SHA256Sum is the expected SHA256 checksum of the plugin binary.
                        Must be a 64-character hexadecimal string.
                      maxLength: 64
                      minLength: 64
                      pattern: ^[0-9a-fA-F]{64}$
                      type: string
                    type:
                      description: Type is the plugin type (e.g., "secret", "auth").
                      minLength: 1
                      type: string
                    version:
                      description: Version is the image version or tag.
                      minLength: 1
                      type: string
                  required:
                  - binaryName
                  - name
                  - sha256sum
                  - type
                  - version
                  type: object
                type: array
              podMetadata:
                description: |-
                  PodMetadata configures additional labels and annotations for the OpenBao Pod template.
                  This is useful for platform integrations that select Pods via metadata, such as
                  Azure Workload Identity. Operator-managed Pod metadata takes precedence.
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    description: |-
                      Annotations are merged into the generated OpenBao Pod template annotations.
                      Operator-managed annotations take precedence if the same key is specified here.
                    type: object
                  labels:
                    additionalProperties:
                      type: string
                    description: |-
                      Labels are merged into the generated OpenBao Pod template labels.
                      Operator-managed labels take precedence if the same key is specified here.
                    type: object
                type: object
              profile:
                allOf:
                - enum:
                  - Hardened
                  - Development
                - enum:
                  - Hardened
                  - Development
                description: |-
                  Profile defines the security posture for this cluster.
                  When set to "Hardened", the operator enforces strict security requirements:
                  - TLS must be External (cert-manager/CSI managed)
                  - Unseal must use external KMS (no static unseal)
                  - SelfInit must be enabled (no root token)
                  When set to "Development", relaxed security is allowed but a security warning
                  condition is set.
                type: string
              replicas:
                default: 3
                description: Replicas is the desired number of OpenBao pods.
                format: int32
                minimum: 1
                type: integer
              restore:
                description: Restore configures optional restore authentication bootstrap
                  for the cluster.
                properties:
                  jwtAuthRole:
                    description: |-
                      JWTAuthRole is the name of the JWT Auth role configured in OpenBao
                      for restore operations. When set, and when spec.selfInit.oidc.enabled is true,
                      the operator bootstraps a restore policy and JWT role bound to the restore ServiceAccount
                      (<cluster-name>-restore-serviceaccount).

                      If OIDC is enabled in SelfInit and this field is empty, a default role
                      named "openbao-operator-restore" will be assumed/created.

                      The role must grant "update" capability on sys/storage/raft/snapshot-force.
                    type: string
                type: object
              securityContext:
                description: |-
                  SecurityContext allows specifying the PodSecurityContext for the OpenBao Pods.
                  If set, these values override the default security context generated by the operator.
                  This is useful for OpenShift (SCC) compatibility or custom security requirements.
                properties:
                  appArmorProfile:
                    description: |-
                      appArmorProfile is the AppArmor options to use by the containers in this pod.
                      Note that this field cannot be set when spec.os.name is windows.
                    properties:
                      localhostProfile:
                        description: |-
                          localhostProfile indicates a profile loaded on the node that should be used.
                          The profile must be preconfigured on the node to work.
                          Must match the loaded name of the profile.
                          Must be set if and only if type is "Localhost".
                        type: string
                      type:
                        description: |-
                          type indicates which kind of AppArmor profile will be applied.
                          Valid options are:
                            Localhost - a profile pre-loaded on the node.
                            RuntimeDefault - the container runtime's default profile.
                            Unconfined - no AppArmor enforcement.
                        type: string
                    required:
                    - type
                    type: object
                  fsGroup:
                    description: |-
                      A special supplemental group that applies to all containers in a pod.
                      Some volume types allow the Kubelet to change the ownership of that volume
                      to be owned by the pod:

                      1. The owning GID will be the FSGroup
                      2. The setgid bit is set (new files created in the volume will be owned by FSGroup)
                      3. The permission bits are OR'd with rw-rw----

                      If unset, the Kubelet will not modify the ownership and permissions of any volume.
                      Note that this field cannot be set when spec.os.name is windows.
                    format: int64
                    type: integer
                  fsGroupChangePolicy:
                    description: |-
                      fsGroupChangePolicy defines behavior of changing ownership and permission of the volume
                      before being exposed inside Pod. This field will only apply to
                      volume types which support fsGroup based ownership(and permissions).
                      It will have no effect on ephemeral volume types such as: secret, configmaps
                      and emptydir.
                      Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used.
                      Note that this field cannot be set when spec.os.name is windows.
                    type: string
                  runAsGroup:
                    description: |-
                      The GID to run the entrypoint of the container process.
                      Uses runtime default if unset.
                      May also be set in SecurityContext.  If set in both SecurityContext and
                      PodSecurityContext, the value specified in SecurityContext takes precedence
                      for that container.
                      Note that this field cannot be set when spec.os.name is windows.
                    format: int64
                    type: integer
                  runAsNonRoot:
                    description: |-
                      Indicates that the container must run as a non-root user.
                      If true, the Kubelet will validate the image at runtime to ensure that it
                      does not run as UID 0 (root) and fail to start the container if it does.
                      If unset or false, no such validation will be performed.
                      May also be set in SecurityContext.  If set in both SecurityContext and
                      PodSecurityContext, the value specified in SecurityContext takes precedence.
                    type: boolean
                  runAsUser:
                    description: |-
                      The UID to run the entrypoint of the container process.
                      Defaults to user specified in image metadata if unspecified.
                      May also be set in SecurityContext.  If set in both SecurityContext and
                      PodSecurityContext, the value specified in SecurityContext takes precedence
                      for that container.
                      Note that this field cannot be set when spec.os.name is windows.
                    format: int64
                    type: integer
                  seLinuxChangePolicy:
                    description: |-
                      seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod.
                      It has no effect on nodes that do not support SELinux or to volumes does not support SELinux.
                      Valid values are "MountOption" and "Recursive".

                      "Recursive" means relabeling of all files on all Pod volumes by the container runtime.
                      This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node.

                      "MountOption" mounts all eligible Pod volumes with `-o context` mount option.
                      This requires all Pods that share the same volume to use the same SELinux label.
                      It is not possible to share the same volume among privileged and unprivileged Pods.
                      Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes
                      whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their
                      CSIDriver instance. Other volumes are always re-labelled recursively.
                      "MountOption" value is allowed only when SELinuxMount feature gate is enabled.

                      If not specified and SELinuxMount feature gate is enabled, "MountOption" is used.
                      If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes
                      and "Recursive" for all other volumes.

                      This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers.

                      All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state.
                      Note that this field cannot be set when spec.os.name is windows.
                    type: string
                  seLinuxOptions:
                    description: |-
                      The SELinux context to be applied to all containers.
                      If unspecified, the container runtime will allocate a random SELinux context for each
                      container.  May also be set in SecurityContext.  If set in
                      both SecurityContext and PodSecurityContext, the value specified in SecurityContext
                      takes precedence for that container.
                      Note that this field cannot be set when spec.os.name is windows.
                    properties:
                      level:
                        description: Level is SELinux level label that applies to
                          the container.
                        type: string
                      role:
                        description: Role is a SELinux role label that applies to
                          the container.
                        type: string
                      type:
                        description: Type is a SELinux type label that applies to
                          the container.
                        type: string
                      user:
                        description: User is a SELinux user label that applies to
                          the container.
                        type: string
                    type: object
                  seccompProfile:
                    description: |-
                      The seccomp options to use by the containers in this pod.
                      Note that this field cannot be set when spec.os.name is windows.
                    properties:
                      localhostProfile:
                        description: |-
                          localhostProfile indicates a profile defined in a file on the node should be used.
                          The profile must be preconfigured on the node to work.
                          Must be a descending path, relative to the kubelet's configured seccomp profile location.
                          Must be set if type is "Localhost". Must NOT be set for any other type.
                        type: string
                      type:
                        description: |-
                          type indicates which kind of seccomp profile will be applied.
                          Valid options are:

                          Localhost - a profile defined in a file on the node should be used.
                          RuntimeDefault - the container runtime default profile should be used.
                          Unconfined - no profile should be applied.
                        type: string
                    required:
                    - type
                    type: object
                  supplementalGroups:
                    description: |-
                      A list of groups applied to the first process run in each container, in
                      addition to the container's primary GID and fsGroup (if specified).  If
                      the SupplementalGroupsPolicy feature is enabled, the
                      supplementalGroupsPolicy field determines whether these are in addition
                      to or instead of any group memberships defined in the container image.
                      If unspecified, no additional groups are added, though group memberships
                      defined in the container image may still be used, depending on the
                      supplementalGroupsPolicy field.
                      Note that this field cannot be set when spec.os.name is windows.
                    items:
                      format: int64
                      type: integer
                    type: array
                    x-kubernetes-list-type: atomic
                  supplementalGroupsPolicy:
                    description: |-
                      Defines how supplemental groups of the first container processes are calculated.
                      Valid values are "Merge" and "Strict". If not specified, "Merge" is used.
                      (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled
                      and the container runtime must implement support for this feature.
                      Note that this field cannot be set when spec.os.name is windows.
                    type: string
                  sysctls:
                    description: |-
                      Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported
                      sysctls (by the container runtime) might fail to launch.
                      Note that this field cannot be set when spec.os.name is windows.
                    items:
                      description: Sysctl defines a kernel parameter to be set
                      properties:
                        name:
                          description: Name of a property to set
                          type: string
                        value:
                          description: Value of a property to set
                          type: string
                      required:
                      - name
                      - value
                      type: object
                    type: array
                    x-kubernetes-list-type: atomic
                  windowsOptions:
                    description: |-
                      The Windows specific settings applied to all containers.
                      If unspecified, the options within a container's SecurityContext will be used.
                      If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
                      Note that this field cannot be set when spec.os.name is linux.
                    properties:
                      gmsaCredentialSpec:
                        description: |-
                          GMSACredentialSpec is where the GMSA admission webhook
                          (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the
                          GMSA credential spec named by the GMSACredentialSpecName field.
                        type: string
                      gmsaCredentialSpecName:
                        description: GMSACredentialSpecName is the name of the GMSA
                          credential spec to use.
                        type: string
                      hostProcess:
                        description: |-
                          HostProcess determines if a container should be run as a 'Host Process' container.
                          All of a Pod's containers must have the same effective HostProcess value
                          (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers).
                          In addition, if HostProcess is true then HostNetwork must also be set to true.
                        type: boolean
                      runAsUserName:
                        description: |-
                          The UserName in Windows to run the entrypoint of the container process.
                          Defaults to the user specified in image metadata if unspecified.
                          May also be set in PodSecurityContext. If set in both SecurityContext and
                          PodSecurityContext, the value specified in SecurityContext takes precedence.
                        type: string
                    type: object
                type: object
              selfInit:
                description: |-
                  SelfInit configures OpenBao's native self-initialization feature.
                  When enabled, OpenBao initializes itself on first start using the configured
                  requests, and the root token is automatically revoked.
                  See: https://openbao.org/docs/configuration/self-init/
                properties:
                  enabled:
                    default: false
                    description: |-
                      Enabled activates OpenBao's self-initialization feature.
                      When true, the Operator injects initialize stanzas into config.hcl
                      and does NOT create a root token Secret (root token is auto-revoked).

                      WARNING: The root token is auto-revoked during initialization. You MUST
                      configure user authentication (e.g., userpass, JWT, Kubernetes auth) via
                      spec.selfInit.requests before enabling this. spec.selfInit.oidc.enabled
                      only provides Operator authentication for lifecycle tasks, NOT user access.
                      Enabling without user authentication results in permanent lockout.
                    type: boolean
                  oidc:
                    description: |-
                      OIDC configures JWT authentication for the Operator to perform cluster
                      lifecycle operations (backups, upgrades, restores). When enabled, this
                      sets up the jwt-operator auth method, OIDC discovery, and operator roles.
                      This is for Operator authentication only - users must configure their own
                      authentication methods via spec.selfInit.requests.
                    properties:
                      audience:
                        description: |-
                          Audience, if set, must match the operator installation audience used for
                          projected OpenBao auth tokens.
                          This field does not create a per-cluster TokenRequest audience override.
                        type: string
                      enabled:
                        description: Enabled triggers the bootstrap logic.
                        type: boolean
                      issuer:
                        description: |-
                          Issuer overrides the auto-discovered K8s issuer URL.
                          Critical for scenarios where OpenBao sees a different K8s URL than the Operator.
                        type: string
                    required:
                    - enabled
                    type: object
                  requests:
                    description: |-
                      Requests defines the API operations to execute during self-initialization.
                      Each request becomes a named request block inside an initialize stanza.
                    items:
                      description: SelfInitRequest defines a single API operation
                        to execute during self-initialization.
                      properties:
                        allowFailure:
                          description: |-
                            AllowFailure allows this request to fail without blocking initialization.
                            Defaults to false.
                          type: boolean
                        auditDevice:
                          description: |-
                            AuditDevice configures an audit device when Path starts with "sys/audit/".
                            This provides structured configuration for audit devices instead of raw JSON.
                            Only used when Path matches the pattern "sys/audit/*".
                          properties:
                            description:
                              description: Description is an optional description
                                for the audit device.
                              type: string
                            fileOptions:
                              description: |-
                                FileOptions configures options for file audit devices.
                                Only used when Type is "file".
                              properties:
                                filePath:
                                  description: |-
                                    FilePath is the path to where the audit log will be written.
                                    Special keywords: "stdout" writes to standard output, "discard" discards output.
                                  minLength: 1
                                  type: string
                                mode:
                                  description: |-
                                    Mode is a string containing an octal number representing the bit pattern for the file mode.
                                    Defaults to "0600" if not specified. Set to "0000" to prevent OpenBao from modifying the file mode.
                                  type: string
                              required:
                              - filePath
                              type: object
                            httpOptions:
                              description: |-
                                HTTPOptions configures options for HTTP audit devices.
                                Only used when Type is "http".
                              properties:
                                headers:
                                  description: |-
                                    Headers is a JSON object describing headers. Must take the shape map[string][]string,
                                    i.e., an object of headers, with each having one or more values.
                                    Headers without values will be ignored.
                                  x-kubernetes-preserve-unknown-fields: true
                                uri:
                                  description: URI is the URI of the remote server
                                    where the audit logs will be written.
                                  minLength: 1
                                  type: string
                              required:
                              - uri
                              type: object
                            socketOptions:
                              description: |-
                                SocketOptions configures options for socket audit devices.
                                Only used when Type is "socket".
                              properties:
                                address:
                                  description: |-
                                    Address is the socket server address to use.
                                    Example: "127.0.0.1:9090" or "/tmp/audit.sock".
                                  type: string
                                socketType:
                                  description: |-
                                    SocketType is the socket type to use, any type compatible with net.Dial is acceptable.
                                    Defaults to "tcp" if not specified.
                                  type: string
                                writeTimeout:
                                  description: |-
                                    WriteTimeout is the (deadline) time in seconds to allow writes to be completed over the socket.
                                    A zero value means that write attempts will not time out.
                                    Defaults to "2s" if not specified.
                                  type: string
                              type: object
                            syslogOptions:
                              description: |-
                                SyslogOptions configures options for syslog audit devices.
                                Only used when Type is "syslog".
                              properties:
                                facility:
                                  description: |-
                                    Facility is the syslog facility to use.
                                    Defaults to "AUTH" if not specified.
                                  type: string
                                tag:
                                  description: |-
                                    Tag is the syslog tag to use.
                                    Defaults to "openbao" if not specified.
                                  type: string
                              type: object
                            type:
                              description: Type is the type of audit device (e.g.,
                                "file", "syslog", "socket", "http").
                              enum:
                              - file
                              - syslog
                              - socket
                              - http
                              minLength: 1
                              type: string
                          required:
                          - type
                          type: object
                        authMethod:
                          description: |-
                            AuthMethod configures an auth method when Path starts with "sys/auth/".
                            This provides structured configuration for enabling auth methods.
                            Only used when Path matches the pattern "sys/auth/*".
                          properties:
                            config:
                              additionalProperties:
                                type: string
                              description: |-
                                Config contains optional configuration for the auth method mount.
                                Common fields include: default_lease_ttl, max_lease_ttl, listing_visibility, etc.
                              type: object
                            description:
                              description: Description is an optional description
                                for the auth method.
                              type: string
                            type:
                              description: Type is the type of auth method (e.g.,
                                "jwt", "kubernetes", "userpass", "ldap").
                              minLength: 1
                              type: string
                          required:
                          - type
                          type: object
                        data:
                          description: |-
                            Data contains the request payload for paths that don't have structured types.
                            This must be a JSON/YAML object whose shape matches the target API endpoint.
                            Nested maps and lists are supported and are rendered into the initialize stanza as HCL objects.

                            **Note:** For common paths, use structured types instead:
                            - `sys/audit/*` → use `auditDevice`
                            - `sys/auth/*` → use `authMethod`
                            - `sys/mounts/*` → use `secretEngine`
                            - `sys/policies/*` → use `policy`

                            This payload is stored in the OpenBaoCluster resource and persisted in etcd;
                            it must not contain sensitive values such as tokens, passwords, or unseal keys.
                          x-kubernetes-preserve-unknown-fields: true
                        name:
                          description: |-
                            Name is a unique identifier for this request (used as the block name).
                            Must match regex ^[A-Za-z_][A-Za-z0-9_-]*$
                          maxLength: 64
                          minLength: 1
                          pattern: ^[A-Za-z_][A-Za-z0-9_-]*$
                          type: string
                        operation:
                          allOf:
                          - enum:
                            - create
                            - read
                            - update
                            - delete
                            - list
                            - patch
                          - enum:
                            - create
                            - read
                            - update
                            - delete
                            - list
                            - patch
                          description: 'Operation is the API operation type: create,
                            read, update, delete, or list.'
                          type: string
                        path:
                          description: Path is the API path to call (e.g., "sys/audit/stdout",
                            "auth/kubernetes/config").
                          minLength: 1
                          type: string
                        policy:
                          description: |-
                            Policy configures a policy when Path starts with "sys/policies/".
                            This provides structured configuration for creating/updating policies.
                            Only used when Path matches the pattern "sys/policies/*".
                          properties:
                            policy:
                              description: |-
                                Policy is the HCL or JSON policy content.
                                This is the actual policy rules that will be applied.
                              minLength: 1
                              type: string
                          required:
                          - policy
                          type: object
                        secretEngine:
                          description: |-
                            SecretEngine configures a secret engine when Path starts with "sys/mounts/".
                            This provides structured configuration for enabling secret engines.
                            Only used when Path matches the pattern "sys/mounts/*".
                          properties:
                            description:
                              description: Description is an optional description
                                for the secret engine.
                              type: string
                            options:
                              additionalProperties:
                                type: string
                              description: |-
                                Options contains optional configuration specific to the secret engine type.
                                For KV engines, common options include: version ("1" or "2").
                                For other engines, options vary by type.
                              type: object
                            type:
                              description: Type is the type of secret engine (e.g.,
                                "kv", "pki", "transit", "database").
                              minLength: 1
                              type: string
                          required:
                          - type
                          type: object
                      required:
                      - name
                      - operation
                      - path
                      type: object
                    type: array
                required:
                - enabled
                type: object
              service:
                description: Service configures the primary Service used to expose
                  OpenBao inside or outside the cluster.
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    description: Annotations are additional annotations to apply to
                      the Service.
                    type: object
                  type:
                    description: Type is the Kubernetes Service type, for example
                      "ClusterIP" or "LoadBalancer".
                    type: string
                type: object
              serviceAccount:
                description: ServiceAccount configures the Kubernetes ServiceAccount
                  used by the OpenBao Pods.
                properties:
                  annotations:
                    additionalProperties:
                      type: string
                    description: |-
                      Annotations to add to the ServiceAccount.
                      Useful for cloud provider Workload Identity (e.g. eks.amazonaws.com/role-arn).
                    type: object
                  name:
                    description: |-
                      Name overrides the generated ServiceAccount name.
                      If not specified, defaults to "<cluster-name>-serviceaccount".
                    type: string
                type: object
              storage:
                description: Storage configures persistent storage for the cluster.
                properties:
                  size:
                    description: Size is the requested persistent volume size, for
                      example "10Gi".
                    minLength: 1
                    type: string
                  storageClassName:
                    description: StorageClassName is an optional StorageClass for
                      the PVCs.
                    type: string
                required:
                - size
                type: object
              telemetry:
                description: |-
                  Telemetry configures telemetry reporting for the OpenBao cluster.
                  See: https://openbao.org/docs/configuration/telemetry/
                properties:
                  circonusAPIApp:
                    description: CirconusAPIApp is the API app name for Circonus.
                    type: string
                  circonusAPIKey:
                    description: |-
                      Circonus-specific options
                      CirconusAPIKey is the API key for Circonus.
                    type: string
                  circonusAPIURL:
                    description: CirconusAPIURL is the API URL for Circonus.
                    type: string
                  circonusBrokerID:
                    description: CirconusBrokerID is the broker ID for Circonus.
                    type: string
                  circonusBrokerSelectTag:
                    description: CirconusBrokerSelectTag is the broker select tag
                      for Circonus.
                    type: string
                  circonusCheckDisplayName:
                    description: CirconusCheckDisplayName is the display name for
                      Circonus.
                    type: string
                  circonusCheckForceMetricActivation:
                    description: CirconusCheckForceMetricActivation forces metric
                      activation in Circonus.
                    type: string
                  circonusCheckID:
                    description: CirconusCheckID is the check ID for Circonus.
                    type: string
                  circonusCheckInstanceID:
                    description: CirconusCheckInstanceID is the instance ID for Circonus.
                    type: string
                  circonusCheckSearchTag:
                    description: CirconusCheckSearchTag is the search tag for Circonus.
                    type: string
                  circonusCheckTags:
                    description: CirconusCheckTags is the tags for Circonus.
                    type: string
                  circonusSubmissionInterval:
                    description: CirconusSubmissionInterval is the submission interval
                      for Circonus.
                    type: string
                  disableHostname:
                    description: DisableHostname specifies if gauge values should
                      be prefixed with the local hostname.
                    type: boolean
                  dogStatsdAddress:
                    description: |-
                      DogStatsD-specific options
                      DogStatsdAddress is the address of the DogStatsD server.
                    type: string
                  dogStatsdTags:
                    description: DogStatsdTags are tags to add to all metrics.
                    items:
                      type: string
                    type: array
                  enableHostnameLabel:
                    description: EnableHostnameLabel specifies if all metric values
                      should contain the host label.
                    type: boolean
                  leaseMetricsEpsilon:
                    description: LeaseMetricsEpsilon specifies the size of the bucket
                      used to measure future lease expiration.
                    type: string
                  maximumGaugeCardinality:
                    description: MaximumGaugeCardinality is the maximum cardinality
                      of gauge labels.
                    format: int32
                    type: integer
                  metricsPrefix:
                    description: MetricsPrefix specifies the prefix used for metric
                      values.
                    type: string
                  prometheusRetentionTime:
                    description: |-
                      Prometheus-specific options
                      PrometheusRetentionTime specifies how long to retain metrics in Prometheus format.
                    type: string
                  stackdriverDebugLogs:
                    description: StackdriverDebugLogs specifies if OpenBao writes
                      additional stackdriver debug logs.
                    type: boolean
                  stackdriverLocation:
                    description: StackdriverLocation is the GCP or AWS region.
                    type: string
                  stackdriverNamespace:
                    description: StackdriverNamespace is a namespace identifier for
                      the telemetry data.
                    type: string
                  stackdriverProjectID:
                    description: |-
                      Stackdriver-specific options
                      StackdriverProjectID is the Google Cloud Project ID.
                    type: string
                  statsdAddress:
                    description: |-
                      StatsD-specific options
                      StatsdAddress is the address of the StatsD server.
                    type: string
                  statsiteAddress:
                    description: |-
                      Statsite-specific options
                      StatsiteAddress is the address of the statsite server.
                    type: string
                  usageGaugePeriod:
                    description: |-
                      Common telemetry options
                      UsageGaugePeriod specifies the interval at which high-cardinality usage data is collected.
                    type: string
                type: object
              tls:
                description: TLS configures TLS for the cluster.
                properties:
                  acme:
                    description: ACME configures settings when Mode is 'ACME'.
                    properties:
                      directoryURL:
                        description: DirectoryURL is the ACME directory URL (e.g.,
                          "https://acme-v02.api.letsencrypt.org/directory").
                        minLength: 1
                        type: string
                      domain:
                        description: |-
                          Domain is the domain name for which to obtain the certificate.
                          Deprecated: use Domains to request a certificate with multiple SANs.
                        minLength: 1
                        type: string
                      domains:
                        description: |-
                          Domains is the list of domain names for which to obtain the certificate.
                          This maps to OpenBao's listener `tls_acme_domains` field.

                          When empty, the operator will default to an internal Service name suitable for
                          private ACME CAs running inside the cluster (e.g., "<cluster>-acme.<namespace>.svc").
                        items:
                          type: string
                        minItems: 1
                        type: array
                      email:
                        description: Email is the email address to use for ACME registration.
                        type: string
                      sharedCache:
                        description: |-
                          SharedCache configures a filesystem cache shared across OpenBao replicas for ACME account
                          and certificate state. This is required for HA ACME topologies where more than one Pod
                          can serve the same hostname concurrently.
                        properties:
                          existingClaimName:
                            description: |-
                              ExistingClaimName is the name of a pre-created RWX PVC in the same namespace.
                              Required when Mode is ExistingPVC.
                            minLength: 1
                            type: string
                          mode:
                            description: Mode selects whether the operator creates
                              a dedicated RWX PVC or mounts an existing one.
                            enum:
                            - ManagedPVC
                            - ExistingPVC
                            type: string
                          size:
                            description: |-
                              Size is the requested capacity for the managed ACME cache PVC.
                              Required when Mode is ManagedPVC.
                            minLength: 1
                            type: string
                          storageClassName:
                            description: StorageClassName is an optional StorageClass
                              for the managed ACME cache PVC.
                            type: string
                        required:
                        - mode
                        type: object
                        x-kubernetes-validations:
                        - message: tls.acme.sharedCache.existingClaimName is only
                            supported when mode is ExistingPVC
                          rule: self.mode != 'ManagedPVC' || !has(self.existingClaimName)
                            || size(self.existingClaimName) == 0
                        - message: tls.acme.sharedCache.existingClaimName is required
                            when mode is ExistingPVC
                          rule: self.mode != 'ExistingPVC' || size(self.existingClaimName)
                            > 0
                        - message: tls.acme.sharedCache.size is only supported when
                            mode is ManagedPVC
                          rule: self.mode != 'ExistingPVC' || !has(self.size) || size(self.size)
                            == 0
                        - message: tls.acme.sharedCache.storageClassName is only supported
                            when mode is ManagedPVC
                          rule: self.mode != 'ExistingPVC' || !has(self.storageClassName)
                            || size(self.storageClassName) == 0
                        - message: tls.acme.sharedCache.size is required when mode
                            is ManagedPVC
                          rule: self.mode != 'ManagedPVC' || size(self.size) > 0
                    required:
                    - directoryURL
                    type: object
                    x-kubernetes-validations:
                    - message: tls.acme.domain and tls.acme.domains are mutually exclusive;
                        use only one
                      rule: '!(has(self.domain) && has(self.domains) && size(self.domains)
                        > 0)'
                  enabled:
                    description: Enabled controls whether TLS is enabled for the cluster.
                    type: boolean
                  extraSANs:
                    description: |-
                      ExtraSANs lists additional subject alternative names to include in server certificates.
                      Only used when Mode is OperatorManaged.
                    items:
                      type: string
                    type: array
                  mode:
                    allOf:
                    - enum:
                      - OperatorManaged
                      - External
                      - ACME
                    - enum:
                      - OperatorManaged
                      - External
                      - ACME
                    default: OperatorManaged
                    description: Mode controls who manages the certificate lifecycle.
                    type: string
                  rotationPeriod:
                    description: |-
                      RotationPeriod is a duration string (for example, "720h") controlling certificate rotation.
                      Only used when Mode is OperatorManaged.
                    minLength: 1
                    type: string
                required:
                - enabled
                type: object
              unseal:
                description: |-
                  Unseal defines the auto-unseal configuration.
                  If omitted, defaults to "static" mode managed by the operator.
                properties:
                  awskms:
                    description: |-
                      AWSKMS configures the AWS KMS seal type.
                      Required when Type is "awskms".
                    properties:
                      accessKey:
                        description: |-
                          AccessKey is the AWS access key ID to use.
                          Note: It is strongly recommended to use CredentialsSecretRef or Workload Identity (IRSA) instead.
                        type: string
                      endpoint:
                        description: |-
                          Endpoint is the KMS API endpoint to be used for AWS KMS requests.
                          Useful when connecting to KMS over a VPC Endpoint.
                        type: string
                      kmsKeyID:
                        description: |-
                          KMSKeyID is the AWS KMS key ID or ARN to use for encryption and decryption.
                          An alias in the format "alias/key-alias-name" may also be used.
                        minLength: 1
                        type: string
                      region:
                        description: Region is the AWS region where the encryption
                          key lives.
                        minLength: 1
                        type: string
                      secretKey:
                        description: |-
                          SecretKey is the AWS secret access key to use.
                          Note: It is strongly recommended to use CredentialsSecretRef or Workload Identity (IRSA) instead.
                        type: string
                      sessionToken:
                        description: SessionToken specifies the AWS session token.
                        type: string
                    required:
                    - kmsKeyID
                    - region
                    type: object
                  azureKeyVault:
                    description: |-
                      AzureKeyVault configures the Azure Key Vault seal type.
                      Required when Type is "azurekeyvault".
                    properties:
                      clientID:
                        description: ClientID is the Azure client ID.
                        type: string
                      clientSecret:
                        description: |-
                          ClientSecret is the Azure client secret.
                          Note: It is strongly recommended to use CredentialsSecretRef or Managed Service Identity instead.
                        type: string
                      environment:
                        description: Environment is the Azure environment (e.g., "AzurePublicCloud",
                          "AzureUSGovernmentCloud").
                        type: string
                      keyName:
                        description: KeyName is the name of the key in the Azure Key
                          Vault.
                        minLength: 1
                        type: string
                      resource:
                        description: |-
                          Resource is the Azure AD resource endpoint.
                          For Managed HSM, this should usually be "managedhsm.azure.net".
                        type: string
                      tenantID:
                        description: TenantID is the Azure tenant ID.
                        type: string
                      vaultName:
                        description: VaultName is the name of the Azure Key Vault.
                        minLength: 1
                        type: string
                    required:
                    - keyName
                    - vaultName
                    type: object
                  credentialsSecretRef:
                    description: |-
                      CredentialsSecretRef references a Secret containing provider credentials
                      (for example AWS access keys, GCP credentials.json, Azure client-secret keys,
                      or OCI SDK config for authTypeAPIKey mode).
                      If using Workload Identity (IRSA, GKE WI, Azure MSI), this can be omitted.
                      The Secret must exist in the same namespace as the OpenBaoCluster.
                      Cross-namespace references are not allowed for security reasons.
                    properties:
                      name:
                        default: ""
                        description: |-
                          Name of the referent.
                          This field is effectively required, but due to backwards compatibility is
                          allowed to be empty. Instances of this type with an empty value here are
                          almost certainly wrong.
                          More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                        type: string
                    type: object
                    x-kubernetes-map-type: atomic
                  gcpCloudKMS:
                    description: |-
                      GCPCloudKMS configures the GCP Cloud KMS seal type.
                      Required when Type is "gcpckms".
                    properties:
                      credentials:
                        description: |-
                          Credentials is the path to the GCP credentials JSON file.
                          Note: It is strongly recommended to use CredentialsSecretRef or Workload Identity instead.
                        type: string
                      cryptoKey:
                        description: CryptoKey is the name of the GCP KMS crypto key.
                        minLength: 1
                        type: string
                      keyRing:
                        description: KeyRing is the name of the GCP KMS key ring.
                        minLength: 1
                        type: string
                      project:
                        description: Project is the GCP project ID.
                        minLength: 1
                        type: string
                      region:
                        description: Region is the GCP region where the key ring lives.
                        minLength: 1
                        type: string
                    required:
                    - cryptoKey
                    - keyRing
                    - project
                    - region
                    type: object
                  kmip:
                    description: |-
                      KMIP configures the KMIP seal type.
                      Required when Type is "kmip".
                    properties:
                      caCert:
                        description: CACert is the path to the CA certificate for
                          KMIP communication.
                        type: string
                      clientCert:
                        description: ClientCert is the path to the client certificate
                          used for KMIP communication.
                        minLength: 1
                        type: string
                      clientKey:
                        description: ClientKey is the path to the private key used
                          for KMIP communication.
                        minLength: 1
                        type: string
                      disabled:
                        description: Disabled disables this seal configuration, for
                          example during seal migration.
                        type: boolean
                      encryptAlg:
                        description: EncryptAlg is the encryption algorithm used for
                          KMIP requests.
                        enum:
                        - AES_GCM
                        - RSA_OAEP_SHA256
                        - RSA_OAEP_SHA384
                        - RSA_OAEP_SHA512
                        type: string
                      endpoint:
                        description: Endpoint is the KMIP server endpoint.
                        minLength: 1
                        type: string
                      kmsKeyID:
                        description: KMSKeyID is the unique identifier of the KMIP
                          key to use.
                        minLength: 1
                        type: string
                      serverName:
                        description: ServerName is the TLS server name to use when
                          connecting to the KMIP endpoint.
                        type: string
                      timeout:
                        description: Timeout is the timeout in seconds for KMIP requests.
                        format: int32
                        minimum: 1
                        type: integer
                      tls12Ciphers:
                        description: |-
                          TLS12Ciphers configures the TLS 1.2 cipher suites to use when connecting
                          to the KMIP endpoint.
                        type: string
                    required:
                    - clientCert
                    - clientKey
                    - endpoint
                    - kmsKeyID
                    type: object
                  ocikms:
                    description: |-
                      OCIKMS configures the OCI KMS seal type.
                      Required when Type is "ocikms".
                    properties:
                      authTypeAPIKey:
                        description: |-
                          AuthTypeAPIKey enables OCI API key authentication through an OCI SDK config file.
                          When false or omitted, OpenBao uses the default OCI principal flow for the runtime
                          environment, such as instance principal.
                        type: boolean
                      cryptoEndpoint:
                        description: CryptoEndpoint is the OCI KMS crypto endpoint.
                        minLength: 1
                        type: string
                      disabled:
                        description: Disabled disables this seal configuration, for
                          example during seal migration.
                        type: boolean
                      keyID:
                        description: KeyID is the OCID of the master encryption key.
                        minLength: 1
                        type: string
                      managementEndpoint:
                        description: ManagementEndpoint is the OCI KMS management
                          endpoint.
                        minLength: 1
                        type: string
                    required:
                    - cryptoEndpoint
                    - keyID
                    - managementEndpoint
                    type: object
                  pkcs11:
                    description: |-
                      PKCS11 configures the PKCS#11 seal type.
                      Required when Type is "pkcs11".
                    properties:
                      disableSoftwareEncryption:
                        description: DisableSoftwareEncryption disables the software
                          encryption fallback.
                        type: boolean
                      disabled:
                        description: Disabled disables this seal configuration, for
                          example during seal migration.
                        type: boolean
                      keyID:
                        description: KeyID is the PKCS#11 key identifier to use instead
                          of KeyLabel.
                        type: string
                      keyLabel:
                        description: KeyLabel is the label for the encryption key
                          used by OpenBao.
                        minLength: 1
                        type: string
                      lib:
                        description: Lib is the path to the PKCS#11 library provided
                          by the HSM vendor.
                        minLength: 1
                        type: string
                      mechanism:
                        description: Mechanism overrides the PKCS#11 wrapping or encryption
                          mechanism.
                        type: string
                      pin:
                        description: |-
                          PIN is the PIN for accessing the HSM token.
                          Note: It is strongly recommended to use CredentialsSecretRef instead of setting this directly.
                        type: string
                      rsaOAEPHash:
                        description: |-
                          RSAOAEPHash specifies the hash algorithm to use for RSA with OAEP padding.
                          Valid values: sha1, sha224, sha256, sha384, sha512.
                        enum:
                        - sha1
                        - sha224
                        - sha256
                        - sha384
                        - sha512
                        type: string
                      slot:
                        description: Slot is the slot number where the HSM token is
                          located.
                        type: string
                      tokenLabel:
                        description: TokenLabel is the token label of the HSM slot
                          to use instead of Slot.
                        type: string
                    required:
                    - keyLabel
                    - lib
                    type: object
                    x-kubernetes-validations:
                    - message: spec.unseal.pkcs11.slot or spec.unseal.pkcs11.tokenLabel
                        is required
                      rule: (has(self.slot) && size(self.slot) > 0) || (has(self.tokenLabel)
                        && size(self.tokenLabel) > 0)
                    - message: spec.unseal.pkcs11.slot and spec.unseal.pkcs11.tokenLabel
                        are mutually exclusive
                      rule: '!(has(self.slot) && size(self.slot) > 0 && has(self.tokenLabel)
                        && size(self.tokenLabel) > 0)'
                  static:
                    description: |-
                      Static configures the static seal type.
                      Optional when Type is "static" (operator provides defaults if omitted).
                    properties:
                      currentKey:
                        description: |-
                          CurrentKey is the path to the static unseal key file.
                          Defaults to "file:///etc/bao/unseal/key" (operator-managed).
                        type: string
                      currentKeyID:
                        description: |-
                          CurrentKeyID is the identifier for the current unseal key.
                          Defaults to "operator-generated-v1" (operator-managed).
                        type: string
                    type: object
                  transit:
                    description: |-
                      Transit configures the Transit seal type.
                      Required when Type is "transit".
                    properties:
                      address:
                        description: Address is the full address to the OpenBao cluster.
                        minLength: 1
                        type: string
                      disableRenewal:
                        description: |-
                          DisableRenewal disables automatic token renewal.
                          Set to true if token lifecycle is managed externally (e.g., by OpenBao Agent).
                        type: boolean
                      keyName:
                        description: KeyName is the transit key to use for encryption
                          and decryption.
                        minLength: 1
                        type: string
                      mountPath:
                        description: MountPath is the mount path to the transit secret
                          engine.
                        minLength: 1
                        type: string
                      namespace:
                        description: Namespace is the namespace path to the transit
                          secret engine.
                        type: string
                      tlsCACert:
                        description: TLSCACert is the path to the CA certificate file
                          for TLS communication.
                        type: string
                      tlsClientCert:
                        description: TLSClientCert is the path to the client certificate
                          for TLS communication.
                        type: string
                      tlsClientKey:
                        description: TLSClientKey is the path to the private key for
                          TLS communication.
                        type: string
                      tlsServerName:
                        description: TLSServerName is the SNI host name to use when
                          connecting via TLS.
                        type: string
                      tlsSkipVerify:
                        description: |-
                          TLSSkipVerify disables verification of TLS certificates.
                          Using this option is highly discouraged and decreases security.
                        type: boolean
                      token:
                        description: |-
                          Token is the OpenBao token to use for authentication.
                          Note: It is strongly recommended to use CredentialsSecretRef instead of setting this directly.
                        type: string
                    required:
                    - address
                    - keyName
                    - mountPath
                    type: object
                  type:
                    default: static
                    description: |-
                      Type specifies the seal type.
                      Defaults to "static".
                    enum:
                    - static
                    - awskms
                    - gcpckms
                    - azurekeyvault
                    - transit
                    - kmip
                    - ocikms
                    - pkcs11
                    type: string
                type: object
              upgrade:
                description: |-
                  Upgrade configures upgrade operations.

                  Built-in upgrade executor Jobs authenticate with JWT auth using the
                  upgrade ServiceAccount (<cluster-name>-upgrade-serviceaccount). If
                  spec.selfInit.oidc.enabled is true and spec.upgrade.jwtAuthRole is empty,
                  the operator assumes or bootstraps the default "openbao-operator-upgrade"
                  role.

                  Pre-upgrade snapshots use spec.backup configuration and backup
                  authentication rather than spec.upgrade credentials.
                properties:
                  blueGreen:
                    description: BlueGreen configures the behavior when Strategy is
                      BlueGreen.
                    properties:
                      autoPromote:
                        default: true
                        description: |-
                          AutoPromote controls whether newly started blue/green upgrades
                          automatically switch traffic and delete the old cluster after sync.
                          If false when an upgrade starts, that upgrade stays in the Syncing
                          phase waiting for an explicit promotion request via spec.upgrade.requests.promote.
                          Changing this field while an upgrade is already in progress affects only
                          future upgrades.
                        type: boolean
                      autoRollback:
                        description: AutoRollback configures automatic rollback behavior.
                        properties:
                          enabled:
                            default: true
                            description: Enabled controls whether automatic rollback
                              is active.
                            type: boolean
                          onJobFailure:
                            default: true
                            description: |-
                              OnJobFailure triggers rollback when job failures exceed MaxJobFailures.
                              Only applies during early phases (before demoting Blue).
                            type: boolean
                          onValidationFailure:
                            default: true
                            description: |-
                              OnValidationFailure triggers automatic abort/rollback if the pre-promotion
                              hook fails.
                            type: boolean
                        required:
                        - enabled
                        type: object
                      maxJobFailures:
                        default: 5
                        description: |-
                          MaxJobFailures is the maximum consecutive job failures before aborting/rolling back.
                          Defaults to 5 if not specified.
                        format: int32
                        minimum: 1
                        type: integer
                      preUpgradeSnapshot:
                        description: |-
                          PreUpgradeSnapshot triggers a backup at the start of an upgrade.
                          Creates a recovery point before any changes are made.
                          Requires spec.backup to be configured.
                        type: boolean
                      verification:
                        description: VerificationConfig allows defining custom health
                          checks before promotion.
                        properties:
                          minSyncDuration:
                            description: |-
                              MinSyncDuration ensures the Green cluster stays healthy as a non-voter
                              for at least this duration before promotion (e.g., "5m").
                            type: string
                          prePromotionHook:
                            description: |-
                              PrePromotionHook specifies a Job template to run before promoting Green.
                              The job must complete successfully (exit 0) for promotion to proceed.
                              If the job fails, the operator either aborts or rolls back automatically
                              when blueGreen.autoRollback.onValidationFailure is enabled; otherwise it
                              holds for manual resolution.
                            properties:
                              args:
                                description: Args are arguments passed to the command.
                                items:
                                  type: string
                                type: array
                              command:
                                description: Command is the command to run.
                                items:
                                  type: string
                                type: array
                              image:
                                description: Image is the container image for the
                                  validation job.
                                minLength: 1
                                type: string
                              timeoutSeconds:
                                default: 300
                                description: 'TimeoutSeconds is the job timeout (default:
                                  300s).'
                                format: int32
                                type: integer
                            required:
                            - image
                            type: object
                        type: object
                    required:
                    - autoPromote
                    type: object
                  image:
                    description: |-
                      Image is the container image to use for upgrade operations.

                      This image is used by Kubernetes Jobs created during upgrades (for example, blue/green
                      cluster orchestration actions). The executor runs inside the tenant namespace and
                      authenticates to OpenBao using a projected ServiceAccount token (JWT auth).

                      If not specified, defaults to "<repo>:X.Y.Z" where <repo> is derived from OPERATOR_UPGRADE_IMAGE_REPOSITORY
                      (default: "ghcr.io/dc-tec/openbao-upgrade") and the tag matches OPERATOR_VERSION.
                    type: string
                  jwtAuthRole:
                    description: |-
                      JWTAuthRole is the name of the JWT Auth role configured in OpenBao
                      for upgrade executor Jobs. The executor authenticates with a projected
                      ServiceAccount token from <cluster-name>-upgrade-serviceaccount.

                      The role must be configured in OpenBao and must grant the permissions
                      required by the selected upgrade strategy, including:
                      - "read" capability on sys/health
                      - "sudo" and "update" capability on sys/step-down
                      - "read" capability on sys/storage/raft/autopilot/state
                      - for Blue/Green, raft join/configuration/remove-peer/promote/demote operations
                      The role must bind to the upgrade ServiceAccount (<cluster-name>-upgrade-serviceaccount),
                      which is automatically created by the operator.

                      If OIDC is enabled in SelfInit and this field is empty, a default role
                      named "openbao-operator-upgrade" will be assumed/created.

                      This is the supported authentication mechanism for built-in upgrade orchestration.
                    type: string
                  preUpgradeSnapshot:
                    description: |-
                      PreUpgradeSnapshot, when true, triggers a backup before any upgrade.
                      When enabled, the upgrade manager will create a backup using the backup
                      configuration (spec.backup.target, spec.backup.image, etc.) and
                      wait for it to complete before proceeding with the upgrade.

                      If the backup fails, the upgrade will be blocked and a Degraded condition
                      will be set with Reason=PreUpgradeBackupFailed.

                      Requires spec.backup to be configured with target, image, and
                      authentication (jwtAuthRole or tokenSecretRef).
                    type: boolean
                  requests:
                    description: |-
                      Requests defines explicit one-shot operator requests for the current
                      upgrade workflow. The operator acts only when a request value changes.
                    properties:
                      promote:
                        description: |-
                          Promote requests promotion of a held blue/green upgrade when changed to a
                          new non-empty value while spec.upgrade.blueGreen.autoPromote=false.

                          The operator compares this value against
                          status.upgradeRequests.lastHandledPromote and acts only when the value
                          changes. Recommended value is an RFC3339 timestamp string.
                        minLength: 1
                        type: string
                      retry:
                        description: |-
                          Retry requests a retry of the current failed rolling upgrade when changed
                          to a new non-empty value.

                          The operator compares this value against status.upgradeRequests.lastHandledRetry
                          and acts only when the value changes. Recommended value is an RFC3339
                          timestamp string.
                        minLength: 1
                        type: string
                      rollback:
                        description: |-
                          Rollback requests a manual abort or rollback of the current blue/green
                          upgrade when changed to a new non-empty value.

                          The operator compares this value against
                          status.upgradeRequests.lastHandledRollback and acts only when the value
                          changes. Recommended value is an RFC3339 timestamp string.
                        minLength: 1
                        type: string
                    type: object
                  strategy:
                    default: RollingUpdate
                    description: Strategy defines the update strategy to use.
                    enum:
                    - RollingUpdate
                    - BlueGreen
                    type: string
                  tokenSecretRef:
                    description: |-
                      TokenSecretRef optionally references a Secret containing an OpenBao API
                      token for future non-JWT upgrade authentication flows.

                      Built-in rolling and blue/green upgrade orchestration does not support
                      token-based authentication. Configure spec.upgrade.jwtAuthRole or enable
                      spec.selfInit.oidc.enabled instead.
                    properties:
                      name:
                        default: ""
                        description: |-
                          Name of the referent.
                          This field is effectively required, but due to backwards compatibility is
                          allowed to be empty. Instances of this type with an empty value here are
                          almost certainly wrong.
                          More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                        type: string
                    type: object
                    x-kubernetes-map-type: atomic
                    x-kubernetes-validations:
                    - message: spec.upgrade.tokenSecretRef is not supported; configure
                        spec.upgrade.jwtAuthRole or enable spec.selfInit.oidc.enabled
                      rule: self == null
                type: object
              version:
                description: |-
                  Version is the semantic OpenBao version, used for upgrade orchestration.
                  The Operator uses static auto-unseal, which requires OpenBao v2.4.0 or later.
                  Versions below 2.4.0 do not support the static seal feature and will fail to start.
                minLength: 1
                type: string
              workloadHardening:
                description: WorkloadHardening configures opt-in workload hardening
                  features.
                properties:
                  appArmorEnabled:
                    description: |-
                      AppArmorEnabled controls whether the operator sets AppArmor profiles on
                      generated Pods and Jobs. Some Kubernetes environments do not support AppArmor;
                      this is opt-in to avoid scheduling failures.
                    type: boolean
                type: object
            required:
            - profile
            - replicas
            - storage
            - tls
            - version
            type: object
            x-kubernetes-validations:
            - message: spec.tls.rotationPeriod is required when spec.tls.mode is OperatorManaged
              rule: self.tls.mode != 'OperatorManaged' || size(self.tls.rotationPeriod)
                > 0
            - message: spec.tls.acme.sharedCache is only supported when spec.tls.mode
                is ACME
              rule: self.tls.mode == 'ACME' || !has(self.tls.acme) || !has(self.tls.acme.sharedCache)
            - message: HA ACME clusters require spec.tls.acme.sharedCache when more
                than one Pod can serve the same hostname
              rule: self.tls.mode != 'ACME' || ((self.replicas <= 1) && (!has(self.upgrade)
                || self.upgrade.strategy != 'BlueGreen')) || (has(self.tls.acme) &&
                has(self.tls.acme.sharedCache))
            - message: spec.upgrade.strategy is immutable after creation; switching
                between RollingUpdate and BlueGreen is not supported.
              rule: '((!has(self.upgrade) || !has(self.upgrade.strategy) || size(self.upgrade.strategy)
                == 0) ? ''RollingUpdate'' : self.upgrade.strategy) == ((!has(oldSelf.upgrade)
                || !has(oldSelf.upgrade.strategy) || size(oldSelf.upgrade.strategy)
                == 0) ? ''RollingUpdate'' : oldSelf.upgrade.strategy)'
            - message: spec.unseal.credentialsSecretRef for ocikms requires spec.unseal.ocikms.authTypeAPIKey=true
              rule: '!has(self.unseal) || self.unseal.type != ''ocikms'' || !has(self.unseal.credentialsSecretRef)
                || (has(self.unseal.ocikms) && has(self.unseal.ocikms.authTypeAPIKey)
                && self.unseal.ocikms.authTypeAPIKey == true)'
          status:
            description: Status defines the observed state of OpenBaoCluster.
            properties:
              activeLeader:
                description: ActiveLeader is the current Raft leader pod name, for
                  example "prod-cluster-0".
                type: string
              adminOps:
                description: AdminOps holds signals owned by the adminops controller
                  (upgrade + backup).
                nullable: true
                properties:
                  lastError:
                    description: LastError is the last adminops-controller error observed
                      for this cluster.
                    properties:
                      at:
                        description: At is when the error was observed (best-effort).
                        format: date-time
                        type: string
                      message:
                        description: Message is a human-readable error message (best-effort).
                        type: string
                      reason:
                        description: Reason is a low-cardinality identifier for the
                          error.
                        type: string
                    type: object
                type: object
              backup:
                description: Backup tracks the state of backups for this cluster.
                properties:
                  consecutiveFailures:
                    description: ConsecutiveFailures is the number of consecutive
                      backup failures.
                    format: int32
                    type: integer
                  lastAttemptScheduledTime:
                    description: |-
                      LastAttemptScheduledTime is the scheduled time of the last backup attempt.
                      It is derived from the cron schedule and used to ensure at-most-once execution
                      per scheduled window.
                    format: date-time
                    type: string
                  lastAttemptTime:
                    description: |-
                      LastAttemptTime is the timestamp of the last backup attempt, regardless of outcome.
                      This is used to avoid retry loops when a scheduled backup fails.
                    format: date-time
                    type: string
                  lastBackupDuration:
                    description: LastBackupDuration is how long the last backup took
                      (e.g., "45s").
                    type: string
                  lastBackupName:
                    description: LastBackupName is the object key/path of the last
                      successful backup.
                    type: string
                  lastBackupSize:
                    description: LastBackupSize is the size in bytes of the last successful
                      backup.
                    format: int64
                    type: integer
                  lastBackupTime:
                    description: LastBackupTime is the timestamp of the last successful
                      backup.
                    format: date-time
                    type: string
                  lastFailureMessage:
                    description: LastFailureMessage is the detailed message for the
                      last backup failure (if applicable).
                    type: string
                  lastFailureReason:
                    description: LastFailureReason is the low-cardinality reason code
                      for the last backup failure (if applicable).
                    type: string
                  nextScheduledBackup:
                    description: NextScheduledBackup is when the next backup is scheduled.
                    format: date-time
                    type: string
                type: object
              blueGreen:
                description: BlueGreen tracks the state of blue/green upgrades (if
                  enabled).
                properties:
                  blueImage:
                    description: |-
                      BlueImage is the container image used by the Blue cluster.
                      This ensures the Blue cluster is not actively upgraded when spec.image changes.
                    type: string
                  blueRevision:
                    description: BlueRevision is the hash/name of the currently active
                      cluster.
                    type: string
                  greenRevision:
                    description: GreenRevision is the hash/name of the next cluster
                      (if upgrade in progress).
                    type: string
                  jobFailureCount:
                    description: |-
                      JobFailureCount tracks consecutive job failures in the current phase.
                      Reset to 0 on phase transition or successful job completion.
                    format: int32
                    type: integer
                  lastJobFailure:
                    description: LastJobFailure records the name of the last failed
                      job for debugging.
                    type: string
                  manualPromotionRequired:
                    description: |-
                      ManualPromotionRequired snapshots whether the current in-flight blue/green
                      upgrade requires an explicit spec.upgrade.requests.promote request before
                      promotion can proceed. It is derived from spec.upgrade.blueGreen.autoPromote
                      when the upgrade starts.
                    type: boolean
                  phase:
                    description: Phase is the current phase of the blue/green upgrade.
                    enum:
                    - Idle
                    - DeployingGreen
                    - JoiningMesh
                    - Syncing
                    - Promoting
                    - DemotingBlue
                    - Cleanup
                    - RollingBack
                    - RollbackCleanup
                    type: string
                  preUpgradeSnapshotJobName:
                    description: PreUpgradeSnapshotJobName is the name of the backup
                      job triggered at upgrade start.
                    type: string
                  rollbackAttempt:
                    description: |-
                      RollbackAttempt increments each time rollback automation is retried.
                      It is used to produce stable, deterministic Job names per attempt.
                    format: int32
                    type: integer
                  rollbackReason:
                    description: RollbackReason records why a rollback was triggered
                      (if any).
                    type: string
                  rollbackStartTime:
                    description: RollbackStartTime is when the rollback was initiated.
                    format: date-time
                    type: string
                  startTime:
                    description: StartTime is when the current phase began.
                    format: date-time
                    type: string
                type: object
              breakGlass:
                description: |-
                  BreakGlass records when the operator has halted quorum-risk automation and requires
                  explicit operator acknowledgment to continue.
                properties:
                  acknowledgedAt:
                    description: AcknowledgedAt records when break glass was acknowledged.
                    format: date-time
                    type: string
                  active:
                    description: Active indicates whether break glass mode is currently
                      active.
                    type: boolean
                  enteredAt:
                    description: EnteredAt is when break glass mode became active.
                    format: date-time
                    type: string
                  message:
                    description: Message provides a short summary of the detected
                      unsafe state.
                    type: string
                  nonce:
                    description: Nonce is the acknowledgment token required to resume
                      automation.
                    type: string
                  reason:
                    description: Reason is a stable, typed reason for entering break
                      glass mode.
                    enum:
                    - RollbackConsensusRepairFailed
                    - RollbackCleanupPeerRemovalFailed
                    type: string
                  steps:
                    description: Steps provides deterministic recovery guidance.
                    items:
                      type: string
                    type: array
                type: object
              conditions:
                description: Conditions represent the current state of the OpenBaoCluster
                  resource.
                items:
                  description: Condition contains details for one aspect of the current
                    state of this API Resource.
                  properties:
                    lastTransitionTime:
                      description: |-
                        lastTransitionTime is the last time the condition transitioned from one status to another.
                        This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
                      format: date-time
                      type: string
                    message:
                      description: |-
                        message is a human readable message indicating details about the transition.
                        This may be an empty string.
                      maxLength: 32768
                      type: string
                    observedGeneration:
                      description: |-
                        observedGeneration represents the .metadata.generation that the condition was set based upon.
                        For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
                        with respect to the current state of the instance.
                      format: int64
                      minimum: 0
                      type: integer
                    reason:
                      description: |-
                        reason contains a programmatic identifier indicating the reason for the condition's last transition.
                        Producers of specific condition types may define expected values and meanings for this field,
                        and whether the values are considered a guaranteed API.
                        The value should be a CamelCase string.
                        This field may not be empty.
                      maxLength: 1024
                      minLength: 1
                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
                      type: string
                    status:
                      description: status of the condition, one of True, False, Unknown.
                      enum:
                      - "True"
                      - "False"
                      - Unknown
                      type: string
                    type:
                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
                      maxLength: 316
                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
                      type: string
                  required:
                  - lastTransitionTime
                  - message
                  - reason
                  - status
                  - type
                  type: object
                type: array
                x-kubernetes-list-map-keys:
                - type
                x-kubernetes-list-type: map
              currentVersion:
                description: CurrentVersion is the OpenBao version currently running
                  on the cluster.
                type: string
              initialized:
                description: |-
                  Initialized indicates whether the OpenBao cluster has been initialized.
                  This is set to true after the first pod is initialized using bao operator init
                  or after self-initialization completes.
                type: boolean
              lastBackupTime:
                description: |-
                  LastBackupTime is the timestamp of the last successful backup, if configured.
                  Deprecated: Use Backup.LastBackupTime instead.
                format: date-time
                type: string
              observedGeneration:
                description: |-
                  ObservedGeneration is the most recent metadata.generation that has been
                  reconciled into this status.
                format: int64
                type: integer
              operationLock:
                description: |-
                  OperationLock prevents concurrent long-running operations (upgrade/backup/restore)
                  from acting on the same cluster at the same time.
                properties:
                  acquiredAt:
                    description: AcquiredAt is when the lock was first acquired.
                    format: date-time
                    type: string
                  holder:
                    description: Holder is a stable identifier for the lock holder
                      (controller/component).
                    type: string
                  message:
                    description: Message provides human-readable context for why the
                      lock is held.
                    type: string
                  operation:
                    description: Operation is the operation currently holding the
                      lock.
                    enum:
                    - Upgrade
                    - Backup
                    - Restore
                    type: string
                  renewedAt:
                    description: RenewedAt is updated when the holder reasserts the
                      lock during reconciliation.
                    format: date-time
                    type: string
                type: object
              phase:
                description: Phase is a high-level summary of the cluster state.
                enum:
                - Initializing
                - Running
                - Upgrading
                - BackingUp
                - Failed
                type: string
              readyReplicas:
                description: ReadyReplicas is the number of replicas that are currently
                  Ready.
                format: int32
                type: integer
              selfInitialized:
                description: |-
                  SelfInitialized indicates whether the cluster was initialized using
                  OpenBao's self-initialization feature. When true, no root token Secret
                  exists for this cluster (the root token was auto-revoked).
                type: boolean
              upgrade:
                description: |-
                  Upgrade tracks the state of an in-progress upgrade (if any).
                  When non-nil, an upgrade is in progress and the UpgradeManager is orchestrating
                  the pod-by-pod rolling update with leader step-down.
                properties:
                  completedPods:
                    description: CompletedPods lists ordinals of pods that have been
                      successfully upgraded.
                    items:
                      format: int32
                      type: integer
                    type: array
                  currentPartition:
                    description: CurrentPartition is the current StatefulSet partition
                      value.
                    format: int32
                    type: integer
                  fromVersion:
                    description: FromVersion is the version being upgraded from.
                    type: string
                  lastErrorAt:
                    description: LastErrorAt is when the last upgrade error was recorded
                      (best-effort).
                    format: date-time
                    type: string
                  lastErrorMessage:
                    description: LastErrorMessage is a human-readable failure message
                      (best-effort).
                    type: string
                  lastErrorReason:
                    description: |-
                      LastErrorReason is a low-cardinality reason describing why the upgrade failed (if it did).
                      When set, the status controller should consider the cluster Degraded.
                    type: string
                  lastStepDownTime:
                    description: LastStepDownTime records when the last leader step-down
                      was performed.
                    format: date-time
                    type: string
                  startedAt:
                    description: StartedAt is when the upgrade began.
                    format: date-time
                    type: string
                  targetVersion:
                    description: TargetVersion is the version being upgraded to.
                    type: string
                required:
                - currentPartition
                - fromVersion
                - targetVersion
                type: object
              upgradeRequests:
                description: |-
                  UpgradeRequests tracks which explicit upgrade request values have already
                  been handled so one-shot requests are edge-triggered instead of level-triggered.
                properties:
                  lastHandledPromote:
                    description: |-
                      LastHandledPromote is the last observed spec.upgrade.requests.promote
                      value that the operator has handled.
                    type: string
                  lastHandledRetry:
                    description: |-
                      LastHandledRetry is the last observed spec.upgrade.requests.retry value
                      that the operator has handled.
                    type: string
                  lastHandledRollback:
                    description: |-
                      LastHandledRollback is the last observed spec.upgrade.requests.rollback
                      value that the operator has handled.
                    type: string
                type: object
              workload:
                description: Workload holds signals owned by the workload controller
                  (infrastructure reconciliation).
                nullable: true
                properties:
                  lastError:
                    description: LastError is the last workload-controller error observed
                      for this cluster.
                    properties:
                      at:
                        description: At is when the error was observed (best-effort).
                        format: date-time
                        type: string
                      message:
                        description: Message is a human-readable error message (best-effort).
                        type: string
                      reason:
                        description: Reason is a low-cardinality identifier for the
                          error.
                        type: string
                    type: object
                type: object
            type: object
        required:
        - spec
        type: object
    served: true
    storage: true
    subresources:
      status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.19.0
  name: openbaorestores.openbao.org
spec:
  group: openbao.org
  names:
    kind: OpenBaoRestore
    listKind: OpenBaoRestoreList
    plural: openbaorestores
    shortNames:
    - obrestore
    singular: openbaorestore
  scope: Namespaced
  versions:
  - additionalPrinterColumns:
    - jsonPath: .spec.cluster
      name: Cluster
      type: string
    - jsonPath: .status.phase
      name: Phase
      type: string
    - jsonPath: .metadata.creationTimestamp
      name: Age
      type: date
    - jsonPath: .status.message
      name: Message
      priority: 1
      type: string
    name: v1alpha1
    schema:
      openAPIV3Schema:
        description: |-
          OpenBaoRestore represents a request to restore an OpenBao cluster from a snapshot.
          This resource is immutable after creation - it acts as a "job request".
        properties:
          apiVersion:
            description: |-
              APIVersion defines the versioned schema of this representation of an object.
              Servers should convert recognized schemas to the latest internal value, and
              may reject unrecognized values.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
            type: string
          kind:
            description: |-
              Kind is a string value representing the REST resource this object represents.
              Servers may infer this from the endpoint the client submits requests to.
              Cannot be updated.
              In CamelCase.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
            type: string
          metadata:
            type: object
          spec:
            description: |-
              OpenBaoRestoreSpec defines the desired state for a restore operation.
              An OpenBaoRestore acts as a "job request" - it is immutable after creation.
            properties:
              cluster:
                description: |-
                  Cluster is the name of the OpenBaoCluster to restore INTO.
                  Must exist in the same namespace as the OpenBaoRestore.
                minLength: 1
                type: string
              force:
                default: false
                description: |-
                  Force allows restore even if the cluster appears unhealthy.
                  This is required for disaster recovery scenarios where the cluster
                  may be in a degraded state.
                type: boolean
              image:
                description: |-
                  Image is the container image to use for restore operations.
                  Defaults to the same image used for backup operations if not specified.
                  If the target OpenBaoCluster has image verification enabled, the operator will verify this image and pin the restore Job to the verified digest.
                minLength: 1
                type: string
              jwtAuthRole:
                description: |-
                  JWTAuthRole is the name of the JWT Auth role configured in OpenBao
                  for restore operations. When set, the restore executor will use JWT Auth
                  (projected ServiceAccount token) instead of a static token.

                  The role must be configured in OpenBao and must grant the "update" capability on
                  sys/storage/raft/snapshot-force. The role must bind to the restore ServiceAccount
                  (<cluster-name>-restore-serviceaccount) in the cluster namespace.

                  If this field is empty and the target OpenBaoCluster has OIDC enabled,
                  the operator will default to using the "openbao-operator-restore" role.
                type: string
              overrideOperationLock:
                default: false
                description: |-
                  OverrideOperationLock allows the restore controller to clear an active cluster
                  operation lock (upgrade/backup) and proceed with restore. This is a break-glass
                  escape hatch intended for disaster recovery.

                  For safety, this requires force: true. When used, the controller emits a Warning
                  event and records a Condition on the OpenBaoRestore.
                type: boolean
              source:
                description: Source defines where the snapshot comes from.
                properties:
                  key:
                    description: |-
                      Key is the full path to the snapshot object in the bucket.
                      For example, "clusters/prod/2025-10-14-120000.snap".
                    minLength: 1
                    type: string
                  target:
                    description: |-
                      Target reuses BackupTarget for storage connection details.
                      This includes endpoint, bucket, region, credentials, etc.
                    properties:
                      azure:
                        description: |-
                          Azure contains Azure Blob Storage specific configuration.
                          Only used when Provider is "azure".
                        properties:
                          container:
                            description: Container is the blob container name. If
                              empty, uses the Bucket field value.
                            type: string
                          storageAccount:
                            description: |-
                              StorageAccount is the Azure storage account name.
                              Required when using Azure provider.
                            minLength: 1
                            type: string
                        type: object
                      bucket:
                        description: Bucket is the bucket or container name.
                        minLength: 1
                        type: string
                      concurrency:
                        default: 3
                        description: |-
                          Concurrency is the number of concurrent parts to upload during multipart uploads.
                          Defaults to 3. Higher values may improve throughput on fast networks but increase
                          memory usage and may overwhelm slower storage backends.
                        format: int32
                        maximum: 10
                        minimum: 1
                        type: integer
                      credentialsSecretRef:
                        description: |-
                          CredentialsSecretRef optionally references a Secret containing credentials for the object store.
                          The Secret must exist in the same namespace as the owning OpenBao resource.
                          Cross-namespace references are not allowed for security reasons.

                          For S3: Expected keys are "accessKeyId" and "secretAccessKey" (optional: "sessionToken", "region", "caCert").
                          For GCS: Expected key is "credentials.json" containing a service account JSON key.
                          For Azure: Expected keys are "accountKey" or "connectionString".
                          Omit this field when relying on ambient workload identity or another default credential chain.
                        properties:
                          name:
                            default: ""
                            description: |-
                              Name of the referent.
                              This field is effectively required, but due to backwards compatibility is
                              allowed to be empty. Instances of this type with an empty value here are
                              almost certainly wrong.
                              More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                            type: string
                        type: object
                        x-kubernetes-map-type: atomic
                      endpoint:
                        description: |-
                          Endpoint is the HTTP(S) endpoint for the object storage service.
                          For S3: Required (e.g., "https://s3.amazonaws.com" or MinIO endpoint).
                          For GCS: Optional (defaults to googleapis.com).
                          For Azure: Optional (derived from StorageAccount if not specified).
                        type: string
                      gcs:
                        description: |-
                          GCS contains Google Cloud Storage specific configuration.
                          Only used when Provider is "gcs".
                        properties:
                          project:
                            description: |-
                              Project is the GCP project ID. Optional if using ADC with default project or
                              if the credentials JSON includes the project.
                            type: string
                        type: object
                      insecureSkipVerify:
                        description: |-
                          InsecureSkipVerify allows skipping TLS verification (useful for MinIO/LocalStack/Azurite with self-signed certs).
                          This applies to all providers that support TLS.
                        type: boolean
                      partSize:
                        default: 10485760
                        description: |-
                          PartSize is the size of each part in multipart uploads (in bytes).
                          Defaults to 10MB (10485760 bytes). Larger values may improve performance for large snapshots
                          on fast networks, while smaller values may be better for slow or unreliable networks.
                        format: int64
                        minimum: 5242880
                        type: integer
                      pathPrefix:
                        description: PathPrefix is an optional prefix within the bucket
                          for this cluster's snapshots.
                        type: string
                      provider:
                        default: s3
                        description: Provider selects the storage backend. Defaults
                          to "s3" for backward compatibility.
                        enum:
                        - s3
                        - gcs
                        - azure
                        type: string
                      region:
                        default: us-east-1
                        description: |-
                          Region is the AWS region to use for S3-compatible clients.
                          For AWS, this should match the bucket region (for example, "eu-west-1").
                          For many S3-compatible stores (MinIO/Ceph), this can be any non-empty value.
                          Only used when Provider is "s3".
                        type: string
                      roleArn:
                        description: |-
                          RoleARN is the IAM role ARN (or S3-compatible equivalent) to assume via Web Identity.
                          When set, backup and restore Jobs mount a projected ServiceAccount token and set the
                          AWS Web Identity environment variables explicitly.
                          Leave this empty when relying on ambient workload identity or provider-managed default credentials instead.
                          Only used when Provider is "s3".
                        type: string
                      usePathStyle:
                        default: false
                        description: |-
                          UsePathStyle controls whether to use path-style addressing (bucket.s3.amazonaws.com/object)
                          or virtual-hosted-style addressing (bucket.s3.amazonaws.com/object).
                          Set to true for MinIO and S3-compatible stores that require path-style.
                          Set to false for AWS S3 (default, as AWS is deprecating path-style).
                          Only used when Provider is "s3".
                        type: boolean
                      workloadIdentity:
                        description: |-
                          WorkloadIdentity optionally applies provider-specific metadata required by cloud workload identity integrations.
                          Use this for ambient identity setups such as EKS Pod Identity or IRSA, GKE Workload Identity, or Azure Workload Identity.
                          When omitted, backup and restore workloads can still use any credentials exposed through the pod's default provider chain.
                        properties:
                          podLabels:
                            additionalProperties:
                              type: string
                            description: |-
                              PodLabels are merged into the generated backup or restore Job pod template.
                              This is typically used for provider-specific selectors such as Azure Workload Identity.
                              Operator-managed labels take precedence if the same key is specified here.
                            type: object
                          serviceAccountAnnotations:
                            additionalProperties:
                              type: string
                            description: |-
                              ServiceAccountAnnotations are merged into the generated backup or restore ServiceAccount.
                              This is typically used for provider-specific bindings such as GKE Workload Identity
                              or webhook-based AWS/Azure workload identity integrations.
                            type: object
                        type: object
                    required:
                    - bucket
                    type: object
                required:
                - key
                - target
                type: object
              tokenSecretRef:
                description: |-
                  TokenSecretRef optionally references a Secret containing an OpenBao API
                  token to use for restore operations (fallback method).

                  The Secret must exist in the same namespace as the OpenBaoRestore.
                  Cross-namespace references are not allowed for security reasons.

                  The token must have permission to update sys/storage/raft/snapshot-force.

                  If JWTAuthRole is set, this field is ignored in favor of JWT Auth.
                properties:
                  name:
                    default: ""
                    description: |-
                      Name of the referent.
                      This field is effectively required, but due to backwards compatibility is
                      allowed to be empty. Instances of this type with an empty value here are
                      almost certainly wrong.
                      More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
                    type: string
                type: object
                x-kubernetes-map-type: atomic
            required:
            - cluster
            - source
            type: object
          status:
            description: OpenBaoRestoreStatus defines the observed state of OpenBaoRestore.
            properties:
              completionTime:
                description: CompletionTime is when the restore operation completed
                  (success or failure).
                format: date-time
                type: string
              conditions:
                description: Conditions represent the latest available observations
                  of the restore's state.
                items:
                  description: Condition contains details for one aspect of the current
                    state of this API Resource.
                  properties:
                    lastTransitionTime:
                      description: |-
                        lastTransitionTime is the last time the condition transitioned from one status to another.
                        This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
                      format: date-time
                      type: string
                    message:
                      description: |-
                        message is a human readable message indicating details about the transition.
                        This may be an empty string.
                      maxLength: 32768
                      type: string
                    observedGeneration:
                      description: |-
                        observedGeneration represents the .metadata.generation that the condition was set based upon.
                        For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
                        with respect to the current state of the instance.
                      format: int64
                      minimum: 0
                      type: integer
                    reason:
                      description: |-
                        reason contains a programmatic identifier indicating the reason for the condition's last transition.
                        Producers of specific condition types may define expected values and meanings for this field,
                        and whether the values are considered a guaranteed API.
                        The value should be a CamelCase string.
                        This field may not be empty.
                      maxLength: 1024
                      minLength: 1
                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
                      type: string
                    status:
                      description: status of the condition, one of True, False, Unknown.
                      enum:
                      - "True"
                      - "False"
                      - Unknown
                      type: string
                    type:
                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
                      maxLength: 316
                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
                      type: string
                  required:
                  - lastTransitionTime
                  - message
                  - reason
                  - status
                  - type
                  type: object
                type: array
                x-kubernetes-list-map-keys:
                - type
                x-kubernetes-list-type: map
              message:
                description: Message provides additional details about the current
                  phase.
                type: string
              phase:
                default: Pending
                description: Phase represents the current phase of the restore operation.
                enum:
                - Pending
                - Validating
                - Running
                - Completed
                - Failed
                type: string
              snapshotKey:
                description: SnapshotKey is the key of the snapshot that was restored.
                type: string
              snapshotSize:
                description: SnapshotSize is the size of the restored snapshot in
                  bytes.
                format: int64
                type: integer
              startTime:
                description: StartTime is when the restore operation started.
                format: date-time
                type: string
            type: object
        required:
        - spec
        type: object
    served: true
    storage: true
    subresources:
      status: {}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.19.0
  name: openbaotenants.openbao.org
spec:
  group: openbao.org
  names:
    kind: OpenBaoTenant
    listKind: OpenBaoTenantList
    plural: openbaotenants
    shortNames:
    - obt
    singular: openbaotenant
  scope: Namespaced
  versions:
  - additionalPrinterColumns:
    - jsonPath: .spec.targetNamespace
      name: Target Namespace
      type: string
    - jsonPath: .status.provisioned
      name: Provisioned
      type: boolean
    - jsonPath: .metadata.creationTimestamp
      name: Age
      type: date
    name: v1alpha1
    schema:
      openAPIV3Schema:
        description: |-
          OpenBaoTenant is the Schema for the openbaotenants API.
          OpenBaoTenant is a governance CRD that explicitly declares which namespace
          should be provisioned with tenant RBAC. This replaces the previous label-based
          approach (openbao.org/tenant=true) to improve security by eliminating the need
          for the Provisioner to have list/watch permissions on namespaces.
        properties:
          apiVersion:
            description: |-
              APIVersion defines the versioned schema of this representation of an object.
              Servers should convert recognized schemas to the latest internal value, and
              may reject unrecognized values.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
            type: string
          kind:
            description: |-
              Kind is a string value representing the REST resource this object represents.
              Servers may infer this from the endpoint the client submits requests to.
              Cannot be updated.
              In CamelCase.
              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
            type: string
          metadata:
            type: object
          spec:
            description: OpenBaoTenantSpec defines the desired state of OpenBaoTenant.
            properties:
              limitRange:
                description: LimitRange defines the limit range to apply to the tenant
                  namespace.
                properties:
                  limits:
                    description: Limits is the list of LimitRangeItem objects that
                      are enforced.
                    items:
                      description: LimitRangeItem defines a min/max usage limit for
                        any resource that matches on kind.
                      properties:
                        default:
                          additionalProperties:
                            anyOf:
                            - type: integer
                            - type: string
                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                            x-kubernetes-int-or-string: true
                          description: Default resource requirement limit value by
                            resource name if resource limit is omitted.
                          type: object
                        defaultRequest:
                          additionalProperties:
                            anyOf:
                            - type: integer
                            - type: string
                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                            x-kubernetes-int-or-string: true
                          description: DefaultRequest is the default resource requirement
                            request value by resource name if resource request is
                            omitted.
                          type: object
                        max:
                          additionalProperties:
                            anyOf:
                            - type: integer
                            - type: string
                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                            x-kubernetes-int-or-string: true
                          description: Max usage constraints on this kind by resource
                            name.
                          type: object
                        maxLimitRequestRatio:
                          additionalProperties:
                            anyOf:
                            - type: integer
                            - type: string
                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                            x-kubernetes-int-or-string: true
                          description: MaxLimitRequestRatio if specified, the named
                            resource must have a request and limit that are both non-zero
                            where limit divided by request is less than or equal to
                            the enumerated value; this represents the max burst for
                            the named resource.
                          type: object
                        min:
                          additionalProperties:
                            anyOf:
                            - type: integer
                            - type: string
                            pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                            x-kubernetes-int-or-string: true
                          description: Min usage constraints on this kind by resource
                            name.
                          type: object
                        type:
                          description: Type of resource that this limit applies to.
                          type: string
                      required:
                      - type
                      type: object
                    type: array
                    x-kubernetes-list-type: atomic
                required:
                - limits
                type: object
              quota:
                description: Quota defines the resource quota to apply to the tenant
                  namespace.
                properties:
                  hard:
                    additionalProperties:
                      anyOf:
                      - type: integer
                      - type: string
                      pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
                      x-kubernetes-int-or-string: true
                    description: |-
                      hard is the set of desired hard limits for each named resource.
                      More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/
                    type: object
                  scopeSelector:
                    description: |-
                      scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota
                      but expressed using ScopeSelectorOperator in combination with possible values.
                      For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched.
                    properties:
                      matchExpressions:
                        description: A list of scope selector requirements by scope
                          of the resources.
                        items:
                          description: |-
                            A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator
                            that relates the scope name and values.
                          properties:
                            operator:
                              description: |-
                                Represents a scope's relationship to a set of values.
                                Valid operators are In, NotIn, Exists, DoesNotExist.
                              type: string
                            scopeName:
                              description: The name of the scope that the selector
                                applies to.
                              type: string
                            values:
                              description: |-
                                An array of string values. If the operator is In or NotIn,
                                the values array must be non-empty. If the operator is Exists or DoesNotExist,
                                the values array must be empty.
                                This array is replaced during a strategic merge patch.
                              items:
                                type: string
                              type: array
                              x-kubernetes-list-type: atomic
                          required:
                          - operator
                          - scopeName
                          type: object
                        type: array
                        x-kubernetes-list-type: atomic
                    type: object
                    x-kubernetes-map-type: atomic
                  scopes:
                    description: |-
                      A collection of filters that must match each object tracked by a quota.
                      If not specified, the quota matches all objects.
                    items:
                      description: A ResourceQuotaScope defines a filter that must
                        match each object tracked by a quota
                      type: string
                    type: array
                    x-kubernetes-list-type: atomic
                type: object
              targetNamespace:
                description: |-
                  TargetNamespace is the name of the namespace to provision with tenant RBAC.
                  The Provisioner will create Role and RoleBinding resources in this namespace
                  to grant the OpenBaoCluster controller permission to manage OpenBaoCluster
                  resources in that namespace.
                minLength: 1
                type: string
            required:
            - targetNamespace
            type: object
          status:
            description: OpenBaoTenantStatus defines the observed state of OpenBaoTenant.
            properties:
              conditions:
                description: Conditions represent the latest available observations
                  of the tenant's state.
                items:
                  description: Condition contains details for one aspect of the current
                    state of this API Resource.
                  properties:
                    lastTransitionTime:
                      description: |-
                        lastTransitionTime is the last time the condition transitioned from one status to another.
                        This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
                      format: date-time
                      type: string
                    message:
                      description: |-
                        message is a human readable message indicating details about the transition.
                        This may be an empty string.
                      maxLength: 32768
                      type: string
                    observedGeneration:
                      description: |-
                        observedGeneration represents the .metadata.generation that the condition was set based upon.
                        For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
                        with respect to the current state of the instance.
                      format: int64
                      minimum: 0
                      type: integer
                    reason:
                      description: |-
                        reason contains a programmatic identifier indicating the reason for the condition's last transition.
                        Producers of specific condition types may define expected values and meanings for this field,
                        and whether the values are considered a guaranteed API.
                        The value should be a CamelCase string.
                        This field may not be empty.
                      maxLength: 1024
                      minLength: 1
                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
                      type: string
                    status:
                      description: status of the condition, one of True, False, Unknown.
                      enum:
                      - "True"
                      - "False"
                      - Unknown
                      type: string
                    type:
                      description: type of condition in CamelCase or in foo.example.com/CamelCase.
                      maxLength: 316
                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
                      type: string
                  required:
                  - lastTransitionTime
                  - message
                  - reason
                  - status
                  - type
                  type: object
                type: array
                x-kubernetes-list-map-keys:
                - type
                x-kubernetes-list-type: map
              lastError:
                description: LastError reports any issues finding the namespace or
                  applying RBAC.
                type: string
              provisioned:
                description: Provisioned indicates if the RBAC has been successfully
                  applied to the target namespace.
                type: boolean
            type: object
        required:
        - spec
        type: object
    served: true
    storage: true
    subresources:
      status: {}
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller
  namespace: openbao-operator-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-leader-election-role
  namespace: openbao-operator-system
rules:
- apiGroups:
  - coordination.k8s.io
  resources:
  - leases
  verbs:
  - get
  - list
  - watch
  - create
  - update
  - patch
  - delete
- apiGroups:
  - ""
  resources:
  - events
  verbs:
  - create
  - patch
- apiGroups:
  - events.k8s.io
  resources:
  - events
  verbs:
  - create
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-openbaocluster-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingadmissionpolicies
  - validatingadmissionpolicybindings
  verbs:
  - get
- apiGroups:
  - gateway.networking.k8s.io
  resources:
  - gateways
  - gatewayclasses
  verbs:
  - get
- nonResourceURLs:
  - /.well-known/openid-configuration
  - /openid/v1/jwks
  - /.well-known/jwks.json
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-openbaorestore-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaorestores
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: openbao-operator-metrics-auth-role
rules:
- apiGroups:
  - authentication.k8s.io
  resources:
  - tokenreviews
  verbs:
  - create
- apiGroups:
  - authorization.k8s.io
  resources:
  - subjectaccessreviews
  verbs:
  - create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: openbao-operator-metrics-reader
rules:
- nonResourceURLs:
  - /metrics
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-openbaocluster-admin-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters
  verbs:
  - '*'
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters/status
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-openbaocluster-editor-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters/status
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-openbaocluster-viewer-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters/status
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
  name: openbao-operator-openbaotenant-editor-role
rules:
- apiGroups:
  - openbao.org
  resources:
  - openbaotenants
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - openbao.org
  resources:
  - openbaotenants/status
  verbs:
  - get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner-role
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
  - update
  - patch
- apiGroups:
  - openbao.org
  resources:
  - openbaotenants
  - openbaotenants/status
  - openbaotenants/finalizers
  verbs:
  - get
  - list
  - watch
  - update
  - patch
- apiGroups:
  - openbao.org
  resources:
  - openbaoclusters
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingadmissionpolicies
  - validatingadmissionpolicybindings
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - resourcequotas
  verbs:
  - create
- apiGroups:
  - ""
  resourceNames:
  - openbao-operator-tenant-quota
  resources:
  - resourcequotas
  verbs:
  - get
  - patch
- apiGroups:
  - ""
  resources:
  - limitranges
  verbs:
  - create
- apiGroups:
  - ""
  resourceNames:
  - openbao-operator-tenant-limits
  resources:
  - limitranges
  verbs:
  - get
  - patch
- apiGroups:
  - rbac.authorization.k8s.io
  resources:
  - roles
  - rolebindings
  verbs:
  - create
- apiGroups:
  - rbac.authorization.k8s.io
  resourceNames:
  - openbao-operator-tenant-role
  - openbao-operator-tenant-secrets-reader
  - openbao-operator-tenant-secrets-writer
  resources:
  - roles
  verbs:
  - get
  - patch
  - delete
- apiGroups:
  - rbac.authorization.k8s.io
  resourceNames:
  - openbao-operator-tenant-rolebinding
  - openbao-operator-tenant-secrets-reader-rolebinding
  - openbao-operator-tenant-secrets-writer-rolebinding
  resources:
  - rolebindings
  verbs:
  - get
  - patch
  - delete
- apiGroups:
  - rbac.authorization.k8s.io
  resourceNames:
  - openbao-operator-tenant-role
  - openbao-operator-tenant-secrets-reader
  - openbao-operator-tenant-secrets-writer
  resources:
  - roles
  verbs:
  - bind
  - escalate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-leader-election-rolebinding
  namespace: openbao-operator-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: openbao-operator-leader-election-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-controller
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner-leader-election-rolebinding
  namespace: openbao-operator-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: openbao-operator-leader-election-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-provisioner
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-metrics-auth-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openbao-operator-metrics-auth-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-controller
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-openbaocluster-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openbao-operator-controller-openbaocluster-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-controller
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-openbaorestore-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openbao-operator-controller-openbaorestore-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-controller
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner-metrics-auth-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openbao-operator-metrics-auth-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-provisioner
  namespace: openbao-operator-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: openbao-operator-provisioner-role
subjects:
- kind: ServiceAccount
  name: openbao-operator-provisioner
  namespace: openbao-operator-system
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller-metrics-service
  namespace: openbao-operator-system
spec:
  ports:
  - name: https
    port: 8443
    protocol: TCP
    targetPort: 8443
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: openbao-operator
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner-metrics-service
  namespace: openbao-operator-system
spec:
  ports:
  - name: https
    port: 8443
    protocol: TCP
    targetPort: 8443
  selector:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/name: openbao-operator
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-controller
  namespace: openbao-operator-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: controller
      app.kubernetes.io/name: openbao-operator
  template:
    metadata:
      annotations:
        kubectl.kubernetes.io/default-container: manager
      labels:
        app.kubernetes.io/component: controller
        app.kubernetes.io/name: openbao-operator
    spec:
      automountServiceAccountToken: false
      containers:
      - args:
        - --leader-elect
        - --health-probe-bind-address=:8081
        - --metrics-bind-address=:8443
        command:
        - /manager
        - controller
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: OPERATOR_SERVICE_ACCOUNT_NAME
          value: openbao-operator-controller
        - name: OPERATOR_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: OPERATOR_VERSION
          value: 0.0.0
        - name: OPENBAO_JWT_AUDIENCE
          value: openbao-internal
        image: ghcr.io/dc-tec/openbao-operator@sha256:573c39693621c546e0df27b0fa17804f4960f55cf137da82f9006b56441c7836
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8081
          initialDelaySeconds: 15
          periodSeconds: 20
        name: manager
        ports:
        - containerPort: 8443
          name: https
          protocol: TCP
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          limits:
            cpu: 500m
            memory: 256Mi
          requests:
            cpu: 50m
            memory: 128Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access
          readOnly: true
        - mountPath: /var/run/secrets/tokens
          name: openbao-token
          readOnly: true
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      serviceAccountName: openbao-operator-controller
      terminationGracePeriodSeconds: 10
      volumes:
      - name: kube-api-access
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              expirationSeconds: 3600
              path: token
          - configMap:
              items:
              - key: ca.crt
                path: ca.crt
              name: kube-root-ca.crt
          - downwardAPI:
              items:
              - fieldRef:
                  fieldPath: metadata.namespace
                path: namespace
      - name: openbao-token
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              audience: openbao-internal
              expirationSeconds: 3600
              path: openbao-token
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: provisioner
    app.kubernetes.io/managed-by: kustomize
    app.kubernetes.io/name: openbao-operator
  name: openbao-operator-provisioner
  namespace: openbao-operator-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: provisioner
      app.kubernetes.io/name: openbao-operator
  template:
    metadata:
      annotations:
        kubectl.kubernetes.io/default-container: manager
      labels:
        app.kubernetes.io/component: provisioner
        app.kubernetes.io/name: openbao-operator
    spec:
      automountServiceAccountToken: false
      containers:
      - args:
        - --leader-elect
        - --health-probe-bind-address=:8081
        - --metrics-bind-address=:8443
        command:
        - /manager
        - provisioner
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: OPERATOR_SERVICE_ACCOUNT_NAME
          value: openbao-operator-controller
        - name: OPERATOR_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: OPERATOR_VERSION
          value: 0.0.0
        image: ghcr.io/dc-tec/openbao-operator@sha256:573c39693621c546e0df27b0fa17804f4960f55cf137da82f9006b56441c7836
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8081
          initialDelaySeconds: 15
          periodSeconds: 20
        name: manager
        ports:
        - containerPort: 8443
          name: https
          protocol: TCP
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8081
          initialDelaySeconds: 5
          periodSeconds: 10
        resources:
          limits:
            cpu: 100m
            memory: 64Mi
          requests:
            cpu: 10m
            memory: 32Mi
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access
          readOnly: true
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      serviceAccountName: openbao-operator-provisioner
      terminationGracePeriodSeconds: 10
      volumes:
      - name: kube-api-access
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              expirationSeconds: 3600
              path: token
          - configMap:
              items:
              - key: ca.crt
                path: ca.crt
              name: kube-root-ca.crt
          - downwardAPI:
              items:
              - fieldRef:
                  fieldPath: metadata.namespace
                path: namespace
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-enforce-managed-image-digests
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - apps
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      resources:
      - statefulsets
    - apiGroups:
      - batch
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      resources:
      - jobs
  validations:
  - expression: '!variables.is_openbao_managed || !variables.digest_enforcement_required
      || (variables.all_container_images_are_digests && variables.all_init_container_images_are_digests)'
    message: Operator-managed workloads with openbao.org/digest-enforcement=required
      must use digest-pinned images (repo@sha256:...) for all containers and initContainers.
  variables:
  - expression: object != null && has(object.metadata) && has(object.metadata.labels)
      && ("app.kubernetes.io/managed-by" in object.metadata.labels) && object.metadata.labels["app.kubernetes.io/managed-by"]
      == "openbao-operator"
    name: is_openbao_managed
  - expression: object != null && has(object.metadata) && has(object.metadata.labels)
      && ("openbao.org/digest-enforcement" in object.metadata.labels) && object.metadata.labels["openbao.org/digest-enforcement"]
      == "required"
    name: digest_enforcement_required
  - expression: |-
      !has(object.spec.template.spec.containers) || object.spec.template.spec.containers.all(c,

        c.image.matches("^.+@sha256:[a-f0-9]{64}$"))
    name: all_container_images_are_digests
  - expression: |-
      !has(object.spec.template.spec.initContainers) || object.spec.template.spec.initContainers.all(c,

        c.image.matches("^.+@sha256:[a-f0-9]{64}$"))
    name: all_init_container_images_are_digests
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-lock-controller-statefulset-mutations
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - apps
      apiVersions:
      - v1
      operations:
      - UPDATE
      resources:
      - statefulsets
  validations:
  - expression: '!variables.is_controller || object.spec.template.spec.volumes ==
      oldObject.spec.template.spec.volumes'
    message: OpenBao controller cannot modify StatefulSet volumes.
  - expression: '!variables.is_controller || object.spec.template.spec.containers.map(c,
      {''name'': c.name, ''command'': c.command, ''args'': c.args}) == oldObject.spec.template.spec.containers.map(c,
      {''name'': c.name, ''command'': c.command, ''args'': c.args})'
    message: OpenBao controller cannot modify container commands or args.
  - expression: |-
      !variables.is_controller || (has(object.spec.template.spec.initContainers) ? object.spec.template.spec.initContainers : [])

        .map(c, {'name': c.name, 'command': c.command, 'args': c.args}) ==
      (has(oldObject.spec.template.spec.initContainers) ? oldObject.spec.template.spec.initContainers : [])

        .map(c, {'name': c.name, 'command': c.command, 'args': c.args})
    message: OpenBao controller cannot modify init container commands or args.
  - expression: |-
      !variables.is_controller || object.spec.template.spec.automountServiceAccountToken ==

        oldObject.spec.template.spec.automountServiceAccountToken
    message: OpenBao controller cannot modify automountServiceAccountToken.
  - expression: '!variables.is_controller || object.spec.template.spec.securityContext
      == oldObject.spec.template.spec.securityContext'
    message: OpenBao controller cannot modify pod securityContext.
  - expression: '!variables.is_controller || object.spec.template.spec.containers.map(c,
      {''name'': c.name, ''securityContext'': c.securityContext, ''volumeMounts'':
      c.volumeMounts}) == oldObject.spec.template.spec.containers.map(c, {''name'':
      c.name, ''securityContext'': c.securityContext, ''volumeMounts'': c.volumeMounts})'
    message: OpenBao controller cannot modify container securityContext or volumeMounts.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.controller_serviceaccount_name'
    name: controller_principal
  - expression: request.userInfo.username == variables.controller_principal
    name: is_controller
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-lock-managed-resource-mutations
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - secrets
      - configmaps
      - services
      - pods
      - endpoints
    - apiGroups:
      - discovery.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - endpointslices
    - apiGroups:
      - apps
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - statefulsets
    - apiGroups:
      - batch
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - jobs
    - apiGroups:
      - policy
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - poddisruptionbudgets
    - apiGroups:
      - rbac.authorization.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - roles
      - rolebindings
    - apiGroups:
      - networking.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - ingresses
      - networkpolicies
    - apiGroups:
      - gateway.networking.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - httproutes
      - backendtlspolicies
    - apiGroups:
      - gateway.networking.k8s.io
      apiVersions:
      - v1alpha2
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - tlsroutes
  validations:
  - expression: '!variables.is_managed || variables.is_operator_controller || variables.is_operator_provisioner
      || (variables.is_kube_controller_manager && (request.operation == "DELETE" ||
      request.resource.resource == "pods")) || (variables.is_system_controller &&
      request.operation == "DELETE") || (variables.is_garbage_collector_sa && (request.operation
      == "DELETE" || request.operation == "UPDATE")) || (variables.is_system_controller
      && request.resource.resource == "pods") || (variables.is_system_controller &&
      variables.is_endpoints_controller_request) || (variables.is_kube_system_controller
      && request.resource.resource == "pods" && (request.operation == "CREATE" ||
      request.operation == "DELETE")) || (variables.is_kube_system_controller && variables.is_endpoints_controller_request)
      || variables.is_kube_system_service_metadata_update || variables.is_cert_manager_secret_request
      || (variables.is_pod_request && variables.is_kubelet_node && request.operation
      == "DELETE") || variables.is_service_registration_label_update || variables.is_kube_system_pod_finalizer_update
      || variables.is_kube_system_job_finalizer_update || (variables.maintenance_enabled
      && variables.is_break_glass_admin)'
    message: Direct modification of OpenBao-managed resources is prohibited; modify
      the parent OpenBaoCluster/OpenBaoTenant instead.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''openbao-operator-provisioner'''
    name: provisioner_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.controller_serviceaccount_name'
    name: controller_principal
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.provisioner_serviceaccount_name'
    name: provisioner_principal
  - expression: |-
      request.operation == "DELETE" ?

        (oldObject != null &&
          has(oldObject.metadata) &&
          has(oldObject.metadata.labels) &&
          ("app.kubernetes.io/managed-by" in oldObject.metadata.labels) &&
          oldObject.metadata.labels["app.kubernetes.io/managed-by"] == "openbao-operator") :
        ((object != null &&
            has(object.metadata) &&
            has(object.metadata.labels) &&
            ("app.kubernetes.io/managed-by" in object.metadata.labels) &&
            object.metadata.labels["app.kubernetes.io/managed-by"] == "openbao-operator") ||
          (oldObject != null &&
            has(oldObject.metadata) &&
            has(oldObject.metadata.labels) &&
            ("app.kubernetes.io/managed-by" in oldObject.metadata.labels) &&
            oldObject.metadata.labels["app.kubernetes.io/managed-by"] == "openbao-operator"))
    name: is_openbao_managed_by_operator
  - expression: |-
      request.operation == "DELETE" ?

        (oldObject != null &&
          has(oldObject.metadata) &&
          has(oldObject.metadata.labels) &&
          oldObject.metadata.labels.exists(k, v, k.startsWith("openbao.org/"))) :
        ((object != null &&
            has(object.metadata) &&
            has(object.metadata.labels) &&
            object.metadata.labels.exists(k, v, k.startsWith("openbao.org/"))) ||
          (oldObject != null &&
            has(oldObject.metadata) &&
            has(oldObject.metadata.labels) &&
            oldObject.metadata.labels.exists(k, v, k.startsWith("openbao.org/"))))
    name: has_openbao_specific_label
  - expression: variables.is_openbao_managed_by_operator && variables.has_openbao_specific_label
    name: is_managed
  - expression: request.userInfo.username == variables.controller_principal
    name: is_operator_controller
  - expression: request.userInfo.username == variables.provisioner_principal
    name: is_operator_provisioner
  - expression: request.userInfo.username == "system:kube-controller-manager" || request.userInfo.username
      == "system:serviceaccount:kube-system:kube-controller-manager"
    name: is_kube_controller_manager
  - expression: request.userInfo.username.startsWith("system:controller:")
    name: is_system_controller
  - expression: |-
      request.userInfo.username in [

        "system:serviceaccount:kube-system:generic-garbage-collector",
        "system:serviceaccount:kube-system:resourcequota-controller",
        "system:serviceaccount:kube-system:statefulset-controller",
        "system:serviceaccount:kube-system:replicaset-controller",
        "system:serviceaccount:kube-system:job-controller",
        "system:serviceaccount:kube-system:cronjob-controller",
        "system:serviceaccount:kube-system:ttl-after-finished-controller"
      ]
    name: is_garbage_collector_sa
  - expression: request.userInfo.groups.exists(g, g == "system:serviceaccounts:kube-system")
      || request.userInfo.username.startsWith("system:serviceaccount:kube-system:")
    name: is_kube_system_controller
  - expression: request.resource.resource in ["endpoints", "endpointslices"]
    name: is_endpoints_controller_request
  - expression: request.userInfo.username.startsWith("system:serviceaccount:cert-manager:")
    name: is_cert_manager
  - expression: variables.is_cert_manager && request.resource.resource == "secrets"
    name: is_cert_manager_secret_request
  - expression: request.kind.group == "" && request.kind.kind == "Pod"
    name: is_pod_request
  - expression: request.kind.group == "batch" && request.kind.kind == "Job"
    name: is_job_request
  - expression: request.kind.group == "" && request.kind.kind == "Service"
    name: is_service_request
  - expression: request.userInfo.username.startsWith("system:node:")
    name: is_kubelet_node
  - expression: |-
      object != null && has(object.metadata) && has(object.metadata.labels) && ("openbao.org/cluster" in object.metadata.labels) ?

        object.metadata.labels["openbao.org/cluster"] :
        (object != null && has(object.metadata) && has(object.metadata.labels) && ("app.kubernetes.io/instance" in object.metadata.labels) ?
          object.metadata.labels["app.kubernetes.io/instance"] : "")
    name: pod_cluster_name
  - expression: |-
      variables.pod_cluster_name != "" && request.userInfo.username ==

        ("system:serviceaccount:" + request.namespace + ":" + variables.pod_cluster_name + "-serviceaccount")
    name: is_cluster_serviceaccount
  - expression: |-
      variables.is_pod_request && request.operation == "UPDATE" && variables.is_cluster_serviceaccount && object.spec == oldObject.spec && (has(object.metadata.annotations) == has(oldObject.metadata.annotations) &&

        (!has(object.metadata.annotations) || object.metadata.annotations == oldObject.metadata.annotations)) &&
      (has(object.metadata.ownerReferences) == has(oldObject.metadata.ownerReferences) &&

        (!has(object.metadata.ownerReferences) || object.metadata.ownerReferences == oldObject.metadata.ownerReferences)) &&
      (has(object.metadata.finalizers) == has(oldObject.metadata.finalizers) &&

        (!has(object.metadata.finalizers) || object.metadata.finalizers == oldObject.metadata.finalizers)) &&
      has(object.metadata.labels) && has(oldObject.metadata.labels) && (

        (
          object.metadata.labels.all(k, v,
            k in [
              "openbao-active",
              "openbao-initialized",
              "openbao-sealed",
              "openbao-perf-standby",
              "openbao-version"
            ] || (k in oldObject.metadata.labels && oldObject.metadata.labels[k] == v)) &&
          oldObject.metadata.labels.all(k, v,
            k in [
              "openbao-active",
              "openbao-initialized",
              "openbao-sealed",
              "openbao-perf-standby",
              "openbao-version"
            ] || (k in object.metadata.labels && object.metadata.labels[k] == v))
        )
      )
    name: is_service_registration_label_update
  - expression: |-
      variables.is_pod_request && request.operation == "UPDATE" && variables.is_kube_system_controller && object.spec == oldObject.spec && (has(object.metadata.labels) == has(oldObject.metadata.labels) &&

        (!has(object.metadata.labels) || object.metadata.labels == oldObject.metadata.labels)) &&
      (has(object.metadata.annotations) == has(oldObject.metadata.annotations) &&

        (!has(object.metadata.annotations) || object.metadata.annotations == oldObject.metadata.annotations)) &&
      (has(object.metadata.ownerReferences) == has(oldObject.metadata.ownerReferences) &&

        (!has(object.metadata.ownerReferences) || object.metadata.ownerReferences == oldObject.metadata.ownerReferences)) &&
      (has(object.metadata.finalizers) || has(oldObject.metadata.finalizers)) && (!has(object.metadata.finalizers) || !has(oldObject.metadata.finalizers) || object.metadata.finalizers != oldObject.metadata.finalizers)
    name: is_kube_system_pod_finalizer_update
  - expression: |-
      variables.is_job_request && request.operation == "UPDATE" && (variables.is_kube_controller_manager || variables.is_system_controller || variables.is_kube_system_controller || variables.is_garbage_collector_sa) && has(oldObject.metadata.deletionTimestamp) && object.spec == oldObject.spec && object.status == oldObject.status && (has(object.metadata.labels) == has(oldObject.metadata.labels) &&

        (!has(object.metadata.labels) || object.metadata.labels == oldObject.metadata.labels)) &&
      (has(object.metadata.annotations) == has(oldObject.metadata.annotations) &&

        (!has(object.metadata.annotations) || object.metadata.annotations == oldObject.metadata.annotations)) &&
      (has(object.metadata.ownerReferences) == has(oldObject.metadata.ownerReferences) &&

        (!has(object.metadata.ownerReferences) || object.metadata.ownerReferences == oldObject.metadata.ownerReferences)) &&
      (has(object.metadata.finalizers) || has(oldObject.metadata.finalizers)) && (!has(object.metadata.finalizers) || !has(oldObject.metadata.finalizers) || object.metadata.finalizers != oldObject.metadata.finalizers)
    name: is_kube_system_job_finalizer_update
  - expression: '(object != null && has(object.metadata) && has(object.metadata.annotations))
      ? object.metadata.annotations : {}'
    name: service_new_annotations
  - expression: '(oldObject != null && has(oldObject.metadata) && has(oldObject.metadata.annotations))
      ? oldObject.metadata.annotations : {}'
    name: service_old_annotations
  - expression: |-
      variables.is_service_request && request.operation == "UPDATE" && variables.is_kube_system_controller && object.spec == oldObject.spec && (has(object.metadata.labels) == has(oldObject.metadata.labels) &&

        (!has(object.metadata.labels) || object.metadata.labels == oldObject.metadata.labels)) &&
      (has(object.metadata.ownerReferences) == has(oldObject.metadata.ownerReferences) &&

        (!has(object.metadata.ownerReferences) || object.metadata.ownerReferences == oldObject.metadata.ownerReferences)) &&
      variables.service_new_annotations.all(k, v,

        !k.startsWith("openbao.org/") ||
        (k in variables.service_old_annotations && variables.service_old_annotations[k] == v)) &&
      variables.service_old_annotations.all(k, v,

        !k.startsWith("openbao.org/") ||
        (k in variables.service_new_annotations && variables.service_new_annotations[k] == v))
    name: is_kube_system_service_metadata_update
  - expression: |-
      request.operation == "DELETE" ?

        (oldObject != null &&
          has(oldObject.metadata) &&
          has(oldObject.metadata.annotations) &&
          ("openbao.org/maintenance" in oldObject.metadata.annotations) &&
          oldObject.metadata.annotations["openbao.org/maintenance"] == "true") :
        ((object != null &&
            has(object.metadata) &&
            has(object.metadata.annotations) &&
            ("openbao.org/maintenance" in object.metadata.annotations) &&
            object.metadata.annotations["openbao.org/maintenance"] == "true") ||
          (oldObject != null &&
            has(oldObject.metadata) &&
            has(oldObject.metadata.annotations) &&
            ("openbao.org/maintenance" in oldObject.metadata.annotations) &&
            oldObject.metadata.annotations["openbao.org/maintenance"] == "true"))
    name: maintenance_enabled
  - expression: '["system:masters"]'
    name: break_glass_admin_groups
  - expression: |-
      variables.break_glass_admin_groups.exists(group,

        request.userInfo.groups.exists(g, g == group))
    name: is_break_glass_admin
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-controller-rbac
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - rbac.authorization.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - roles
      - rolebindings
  validations:
  - expression: |-
      !variables.is_controller || request.resource.resource != "roles" || request.operation == "DELETE" || (

        object != null &&
        has(object.metadata) &&
        has(object.metadata.name) &&
        object.metadata.name.endsWith("-role") &&
        has(object.rules) &&
        object.rules != null &&
        (
          (
            variables.component_label in ["backup", "restore"] &&
            object.rules.size() == 1 &&
            object.rules[0].apiGroups == [""] &&
            object.rules[0].resources == ["pods"] &&
            has(object.rules[0].verbs) &&
            object.rules[0].verbs != null &&
            object.rules[0].verbs.all(v, v in ["get", "list", "watch"]) &&
            object.rules[0].verbs.exists(v, v == "get") &&
            object.rules[0].verbs.exists(v, v == "list") &&
            object.rules[0].verbs.exists(v, v == "watch")
          ) ||
          (
            variables.component_label == "" &&
            object.rules.size() == 2 &&
            object.rules.all(r, r.apiGroups == [""] && r.resources == ["pods"] && has(r.verbs) && r.verbs != null) &&
            object.rules.exists(r,
              r.verbs.all(v, v in ["get", "list", "watch"]) &&
              r.verbs.exists(v, v == "get") &&
              r.verbs.exists(v, v == "list") &&
              r.verbs.exists(v, v == "watch") &&
              (!has(r.resourceNames) || r.resourceNames == null || r.resourceNames.size() == 0)
            ) &&
            object.rules.exists(r,
              r.verbs.all(v, v in ["patch", "update"]) &&
              (r.verbs.exists(v, v == "patch") || r.verbs.exists(v, v == "update")) &&
              has(r.resourceNames) &&
              r.resourceNames != null &&
              r.resourceNames.size() > 0
            )
          )
        )
      )
    message: The controller can only create/update Roles for pod discovery (and service
      registration label updates for the OpenBao service account).
  - expression: |-
      !variables.is_controller || request.resource.resource != "rolebindings" || request.operation == "DELETE" || (

        object != null &&
        has(object.metadata) &&
        has(object.metadata.name) &&
        object.metadata.name.endsWith("-rolebinding") &&
        has(object.roleRef) &&
        object.roleRef.apiGroup == "rbac.authorization.k8s.io" &&
        object.roleRef.kind == "Role" &&
        has(object.roleRef.name) &&
        has(object.subjects) &&
        object.subjects != null &&
        object.subjects.size() == 1 &&
        object.subjects[0].kind == "ServiceAccount" &&
        (!has(object.subjects[0].apiGroup) || object.subjects[0].apiGroup == "") &&
        object.subjects[0].namespace == request.namespace &&
        object.metadata.name == object.subjects[0].name + "-rolebinding" &&
        object.roleRef.name == object.subjects[0].name + "-role"
      )
    message: The controller can only create/update RoleBindings that bind a ServiceAccount
      to its own pod-discovery Role.
  - expression: |-
      !variables.is_controller || request.operation != "DELETE" || request.resource.resource != "roles" || (

        oldObject != null &&
        has(oldObject.metadata) &&
        has(oldObject.metadata.name) &&
        oldObject.metadata.name.endsWith("-role")
      )
    message: The controller can only delete Roles for pod discovery.
  - expression: |-
      !variables.is_controller || request.operation != "DELETE" || request.resource.resource != "rolebindings" || (

        oldObject != null &&
        has(oldObject.metadata) &&
        has(oldObject.metadata.name) &&
        oldObject.metadata.name.endsWith("-rolebinding")
      )
    message: The controller can only delete RoleBindings for pod discovery.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.controller_serviceaccount_name'
    name: controller_principal
  - expression: request.userInfo.username == variables.controller_principal
    name: is_controller
  - expression: |-
      request.operation == "DELETE" ?

        (oldObject != null && has(oldObject.metadata) && has(oldObject.metadata.labels) && ("openbao.org/component" in oldObject.metadata.labels) ?
          oldObject.metadata.labels["openbao.org/component"] : "") :
        (object != null && has(object.metadata) && has(object.metadata.labels) && ("openbao.org/component" in object.metadata.labels) ?
          object.metadata.labels["openbao.org/component"] :
          (oldObject != null && has(oldObject.metadata) && has(oldObject.metadata.labels) && ("openbao.org/component" in oldObject.metadata.labels) ?
            oldObject.metadata.labels["openbao.org/component"] : ""))
    name: component_label
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-controller-secret-writes
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - secrets
  validations:
  - expression: |-
      !variables.is_controller || (

        variables.has_required_labels &&
        variables.has_allowed_name
      )
    message: The controller can only create, update, or delete operator-managed Secret
      objects with the fixed cluster-scoped names and labels.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.controller_serviceaccount_name'
    name: controller_principal
  - expression: request.userInfo.username == variables.controller_principal
    name: is_controller
  - expression: |-
      request.operation == "DELETE" ?

        ((
          oldObject != null &&
          has(oldObject.metadata) &&
          oldObject.metadata.name != ''
        ) ? oldObject.metadata.name : request.name) :
        ((
          object != null &&
          has(object.metadata) &&
          object.metadata.name != ''
        ) ? object.metadata.name : request.name)
    name: secret_name
  - expression: |-
      request.operation == "DELETE" ?

        ((
          oldObject != null &&
          has(oldObject.metadata) &&
          has(oldObject.metadata.labels)
        ) ? oldObject.metadata.labels : {}) :
        ((
          object != null &&
          has(object.metadata) &&
          has(object.metadata.labels)
        ) ? object.metadata.labels : {})
    name: secret_labels
  - expression: |-
      ("openbao.org/cluster" in variables.secret_labels) ?

        variables.secret_labels["openbao.org/cluster"] : ""
    name: cluster_name
  - expression: |-
      variables.cluster_name != "" && ("app.kubernetes.io/managed-by" in variables.secret_labels) && variables.secret_labels["app.kubernetes.io/managed-by"] == "openbao-operator" && (

        !("app.kubernetes.io/name" in variables.secret_labels) ||
        variables.secret_labels["app.kubernetes.io/name"] == "openbao"
      ) && (

        !("app.kubernetes.io/instance" in variables.secret_labels) ||
        variables.secret_labels["app.kubernetes.io/instance"] == variables.cluster_name
      )
    name: has_required_labels
  - expression: |-
      variables.cluster_name != "" && variables.secret_name in [

        variables.cluster_name + "-tls-ca",
        variables.cluster_name + "-tls-server",
        variables.cluster_name + "-root-token",
        variables.cluster_name + "-unseal-key"
      ]
    name: has_allowed_name
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-controller-serviceaccounts
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - serviceaccounts
  validations:
  - expression: |-
      !variables.is_controller || (

        variables.has_required_labels &&
        variables.has_allowed_name
      )
    message: The controller can only create, update, or delete operator-managed ServiceAccounts
      for the main OpenBao workload or the fixed backup, restore, and upgrade ServiceAccount
      names.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.controller_serviceaccount_name'
    name: controller_principal
  - expression: request.userInfo.username == variables.controller_principal
    name: is_controller
  - expression: |-
      request.operation == "DELETE" ?

        ((
          oldObject != null &&
          has(oldObject.metadata) &&
          oldObject.metadata.name != ''
        ) ? oldObject.metadata.name : request.name) :
        ((
          object != null &&
          has(object.metadata) &&
          object.metadata.name != ''
        ) ? object.metadata.name : request.name)
    name: serviceaccount_name
  - expression: |-
      request.operation == "DELETE" ?

        ((
          oldObject != null &&
          has(oldObject.metadata) &&
          has(oldObject.metadata.labels)
        ) ? oldObject.metadata.labels : {}) :
        ((
          object != null &&
          has(object.metadata) &&
          has(object.metadata.labels)
        ) ? object.metadata.labels : {})
    name: serviceaccount_labels
  - expression: |-
      ("openbao.org/cluster" in variables.serviceaccount_labels) ?

        variables.serviceaccount_labels["openbao.org/cluster"] : ""
    name: cluster_name
  - expression: |-
      ("openbao.org/service-account-role" in variables.serviceaccount_labels) ?

        variables.serviceaccount_labels["openbao.org/service-account-role"] : ""
    name: serviceaccount_role
  - expression: |-
      ("openbao.org/component" in variables.serviceaccount_labels) ?

        variables.serviceaccount_labels["openbao.org/component"] : ""
    name: component_label
  - expression: |-
      variables.cluster_name != "" && variables.serviceaccount_role in ["main", "backup", "restore", "upgrade"] && ("app.kubernetes.io/managed-by" in variables.serviceaccount_labels) && variables.serviceaccount_labels["app.kubernetes.io/managed-by"] == "openbao-operator" && (

        !("app.kubernetes.io/name" in variables.serviceaccount_labels) ||
        variables.serviceaccount_labels["app.kubernetes.io/name"] == "openbao"
      ) && (

        !("app.kubernetes.io/instance" in variables.serviceaccount_labels) ||
        variables.serviceaccount_labels["app.kubernetes.io/instance"] == variables.cluster_name
      ) && (

        (variables.serviceaccount_role == "main" && variables.component_label == "") ||
        (variables.serviceaccount_role == "backup" && variables.component_label == "backup") ||
        (variables.serviceaccount_role == "restore" && variables.component_label == "restore") ||
        (variables.serviceaccount_role == "upgrade" && variables.component_label == "upgrade")
      )
    name: has_required_labels
  - expression: |-
      variables.cluster_name != "" && (

        (variables.serviceaccount_role == "main" && variables.serviceaccount_name != "") ||
        (variables.serviceaccount_role == "backup" && variables.serviceaccount_name == variables.cluster_name + "-backup-serviceaccount") ||
        (variables.serviceaccount_role == "restore" && variables.serviceaccount_name == variables.cluster_name + "-restore-serviceaccount") ||
        (variables.serviceaccount_role == "upgrade" && variables.serviceaccount_name == variables.cluster_name + "-upgrade-serviceaccount")
      )
    name: has_allowed_name
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-provisioner-namespace-mutations
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - UPDATE
      resources:
      - namespaces
  validations:
  - expression: '!variables.is_provisioner || !variables.is_system_namespace'
    message: The Provisioner may not mutate system namespaces.
  - expression: |-
      !variables.is_provisioner || request.operation != "UPDATE" || request.resource.resource != "namespaces" || (

        oldObject != null &&
        object != null &&
        object.spec == oldObject.spec &&
        object.status == oldObject.status &&
        (has(object.metadata.annotations) == has(oldObject.metadata.annotations) &&
          (!has(object.metadata.annotations) || object.metadata.annotations == oldObject.metadata.annotations)) &&
        (has(object.metadata.ownerReferences) == has(oldObject.metadata.ownerReferences) &&
          (!has(object.metadata.ownerReferences) || object.metadata.ownerReferences == oldObject.metadata.ownerReferences)) &&
        (has(object.metadata.finalizers) == has(oldObject.metadata.finalizers) &&
          (!has(object.metadata.finalizers) || object.metadata.finalizers == oldObject.metadata.finalizers)) &&
        ("pod-security.kubernetes.io/enforce" in variables.new_labels) &&
        ("pod-security.kubernetes.io/audit" in variables.new_labels) &&
        ("pod-security.kubernetes.io/warn" in variables.new_labels) &&
        variables.new_labels["pod-security.kubernetes.io/enforce"] == "restricted" &&
        variables.new_labels["pod-security.kubernetes.io/audit"] == "restricted" &&
        variables.new_labels["pod-security.kubernetes.io/warn"] == "restricted" &&
        (
          variables.new_labels.all(k, v,
            k in [
              "pod-security.kubernetes.io/enforce",
              "pod-security.kubernetes.io/audit",
              "pod-security.kubernetes.io/warn"
            ] || (k in variables.old_labels && variables.old_labels[k] == v)) &&
          variables.old_labels.all(k, v,
            k in [
              "pod-security.kubernetes.io/enforce",
              "pod-security.kubernetes.io/audit",
              "pod-security.kubernetes.io/warn"
            ] || (k in variables.new_labels && variables.new_labels[k] == v))
        )
      )
    message: The Provisioner may only enforce Pod Security Standards labels (restricted)
      on Namespaces.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-provisioner'''
    name: provisioner_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.provisioner_serviceaccount_name'
    name: provisioner_principal
  - expression: request.userInfo.username == variables.provisioner_principal
    name: is_provisioner
  - expression: request.name == variables.operator_namespace || request.name.startsWith("kube-")
      || request.name == "openshift" || request.name.startsWith("openshift-") || request.name.startsWith("gke-")
    name: is_system_namespace
  - expression: '(object != null && has(object.metadata) && has(object.metadata.labels))
      ? object.metadata.labels : {}'
    name: new_labels
  - expression: '(oldObject != null && has(oldObject.metadata) && has(oldObject.metadata.labels))
      ? oldObject.metadata.labels : {}'
    name: old_labels
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-provisioner-rbac
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - rbac.authorization.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - roles
      - rolebindings
  validations:
  - expression: |-
      !variables.is_provisioner || !(

        request.namespace == variables.operator_namespace ||
        request.namespace.startsWith('kube-') ||
        request.namespace == 'openshift' ||
        request.namespace.startsWith('openshift-') ||
        request.namespace.startsWith('gke-') ||
        request.namespace.startsWith('eks-') ||
        request.namespace.startsWith('aws-') ||
        request.namespace.startsWith('aks-') ||
        request.namespace.startsWith('azure-')
      )
    message: The Provisioner may not manage tenant RBAC in system namespaces.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || object.metadata.name in [

        'openbao-operator-tenant-role',
        'openbao-operator-tenant-secrets-reader',
        'openbao-operator-tenant-secrets-writer'
      ]
    message: The Provisioner can only create Roles for the operator tenant template
      (tenant + secrets allowlists).
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || object.metadata.name != 'openbao-operator-tenant-role' || (

        has(object.rules) &&
        object.rules != null &&
        !object.rules.exists(rule,
          has(rule.nonResourceURLs) &&
          rule.nonResourceURLs != null &&
          rule.nonResourceURLs.size() > 0
        ) &&
        object.rules.all(rule,
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'openbao.org' &&
            rule.resources != null &&
            (
              (rule.resources.size() == 3 && rule.resources.all(r, r in ['openbaoclusters', 'openbaoclusters/status', 'openbaoclusters/finalizers'])) ||
              (rule.resources.size() == 3 && rule.resources.all(r, r in ['openbaorestores', 'openbaorestores/status', 'openbaorestores/finalizers']))
            ) &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'apps' &&
            rule.resources != null &&
            rule.resources.size() == 1 &&
            rule.resources[0] == 'statefulsets' &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == '' &&
            rule.resources != null &&
            (
              (
                rule.resources.size() > 0 &&
                rule.resources.all(r, r in ['services', 'configmaps', 'serviceaccounts', 'persistentvolumeclaims']) &&
                rule.verbs != null &&
                rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
              ) ||
              (
                rule.resources.size() == 1 &&
                rule.resources[0] == 'pods' &&
                rule.verbs != null &&
                rule.verbs.all(v, v in ['delete', 'get', 'list', 'patch', 'update', 'watch'])
              ) ||
              (
                rule.resources.size() == 1 &&
                rule.resources[0] == 'endpoints' &&
                rule.verbs != null &&
                rule.verbs.all(v, v in ['get', 'list', 'watch'])
              ) ||
              (
                rule.resources.size() == 1 &&
                rule.resources[0] == 'events' &&
                rule.verbs != null &&
                rule.verbs.all(v, v in ['create', 'patch'])
              )
            )
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'events.k8s.io' &&
            rule.resources != null &&
            rule.resources.size() == 1 &&
            rule.resources[0] == 'events' &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'patch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'discovery.k8s.io' &&
            rule.resources != null &&
            rule.resources.size() == 1 &&
            rule.resources[0] == 'endpointslices' &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['get', 'list', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'batch' &&
            rule.resources != null &&
            rule.resources.size() == 1 &&
            rule.resources[0] == 'jobs' &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'policy' &&
            rule.resources != null &&
            rule.resources.size() == 1 &&
            rule.resources[0] == 'poddisruptionbudgets' &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'networking.k8s.io' &&
            rule.resources != null &&
            rule.resources.size() > 0 &&
            rule.resources.all(r, r in ['ingresses', 'networkpolicies']) &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'gateway.networking.k8s.io' &&
            rule.resources != null &&
            rule.resources.size() > 0 &&
            rule.resources.all(r, r in ['httproutes', 'tlsroutes', 'backendtlspolicies']) &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          ) ||
          (
            rule.apiGroups.size() == 1 &&
            rule.apiGroups[0] == 'rbac.authorization.k8s.io' &&
            rule.resources != null &&
            rule.resources.size() > 0 &&
            rule.resources.all(r, r in ['roles', 'rolebindings']) &&
            rule.verbs != null &&
            rule.verbs.all(v, v in ['create', 'delete', 'get', 'list', 'patch', 'update', 'watch'])
          )
        )
      )
    message: The Provisioner can only create the tenant Role with an allowlisted set
      of API groups, resources, and verbs (no pods/exec, serviceaccounts/token, nonResourceURLs,
      or other unexpected permissions).
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'rolebindings' || object.metadata.name in [

        'openbao-operator-tenant-rolebinding',
        'openbao-operator-tenant-secrets-reader-rolebinding',
        'openbao-operator-tenant-secrets-writer-rolebinding'
      ]
    message: The Provisioner can only create RoleBindings for the operator tenant
      template (tenant + secrets allowlists).
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'rolebindings' || object.roleRef.name in [

        'openbao-operator-tenant-role',
        'openbao-operator-tenant-secrets-reader',
        'openbao-operator-tenant-secrets-writer'
      ]
    message: The Provisioner is restricted to binding only the operator tenant template
      Roles (tenant + secrets allowlists).
  - expression: '!variables.is_provisioner || request.operation == ''DELETE'' || request.resource.resource
      != ''rolebindings'' || object.subjects.all(s, s.kind == ''ServiceAccount'')'
    message: The Provisioner can only grant permissions to ServiceAccounts.
  - expression: '!variables.is_provisioner || request.operation == ''DELETE'' || request.resource.resource
      != ''rolebindings'' || object.subjects.all(s, !has(s.apiGroup) || s.apiGroup
      == '''')'
    message: The Provisioner can only bind to core ServiceAccount subjects (apiGroup
      must be empty).
  - expression: '!variables.is_provisioner || request.operation == ''DELETE'' || request.resource.resource
      != ''rolebindings'' || (object.roleRef.kind == ''Role'' && object.roleRef.apiGroup
      == ''rbac.authorization.k8s.io'')'
    message: The Provisioner can only create RoleBindings that reference a Role (not
      ClusterRole).
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'rolebindings' || (

        (
          object.metadata.name == 'openbao-operator-tenant-rolebinding' &&
          object.roleRef.name == 'openbao-operator-tenant-role' &&
          object.subjects.size() == 1 &&
          object.subjects[0].kind == 'ServiceAccount' &&
          object.subjects[0].name == variables.controller_serviceaccount_name &&
          object.subjects[0].namespace == variables.operator_namespace
        ) ||
        (
          object.metadata.name == 'openbao-operator-tenant-secrets-reader-rolebinding' &&
          object.roleRef.name == 'openbao-operator-tenant-secrets-reader' &&
          object.subjects.size() == 1 &&
          object.subjects[0].kind == 'ServiceAccount' &&
          object.subjects[0].name == variables.controller_serviceaccount_name &&
          object.subjects[0].namespace == variables.operator_namespace
        ) ||
        (
          object.metadata.name == 'openbao-operator-tenant-secrets-writer-rolebinding' &&
          object.roleRef.name == 'openbao-operator-tenant-secrets-writer' &&
          object.subjects.size() == 1 &&
          object.subjects[0].kind == 'ServiceAccount' &&
          object.subjects[0].name == variables.controller_serviceaccount_name &&
          object.subjects[0].namespace == variables.operator_namespace
        )
      )
    message: The Provisioner can only bind tenant RBAC to the operator controller
      ServiceAccount.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || !has(object.rules) || object.rules == null || !object.rules.exists(rule,

        has(rule.verbs) &&
        rule.verbs != null &&
        rule.verbs.exists(v,
          v == 'impersonate' ||
          v == 'bind' ||
          v == 'escalate' ||
          v == '*'
        )
      )
    message: The Provisioner cannot create Roles granting 'impersonate', 'bind', 'escalate',
      or wildcard permissions.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || !has(object.rules) || object.rules == null || !object.rules.exists(rule,

        (
          has(rule.apiGroups) &&
          rule.apiGroups != null &&
          rule.apiGroups.exists(g, g == '*')
        ) ||
        (
          has(rule.resources) &&
          rule.resources != null &&
          rule.resources.exists(r, r == '*')
        )
      )
    message: The Provisioner cannot create Roles with wildcard apiGroups or resources.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || !has(object.rules) || object.rules == null || !object.rules.exists(rule,

        has(rule.nonResourceURLs) &&
        rule.nonResourceURLs != null &&
        rule.nonResourceURLs.size() > 0
      )
    message: The Provisioner cannot create Roles granting nonResourceURLs.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || !has(object.rules) || object.rules == null || !object.rules.exists(rule,

        has(rule.resources) &&
        rule.resources != null &&
        rule.resources.exists(r, r == 'secrets') &&
        !(object.metadata.name in [
          'openbao-operator-tenant-secrets-reader',
          'openbao-operator-tenant-secrets-writer'
        ])
      )
    message: The Provisioner can only grant Secrets permissions via the dedicated
      secrets allowlist Roles.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || object.metadata.name != 'openbao-operator-tenant-secrets-reader' || (

        has(object.rules) &&
        object.rules != null &&
        object.rules.size() == 1 &&
        object.rules[0].apiGroups == [''] &&
        object.rules[0].resources == ['secrets'] &&
        object.rules[0].verbs == ['get'] &&
        has(object.rules[0].resourceNames) &&
        object.rules[0].resourceNames != null &&
        object.rules[0].resourceNames.size() > 0
      )
    message: The Provisioner can only create the secrets reader Role with a non-empty,
      name-scoped (resourceNames) get-only rule.
  - expression: |-
      !variables.is_provisioner || request.operation == 'DELETE' || request.resource.resource != 'roles' || object.metadata.name != 'openbao-operator-tenant-secrets-writer' || (

        has(object.rules) &&
        object.rules != null &&
        object.rules.size() == 2 &&
        object.rules.all(r, r.apiGroups == [''] && r.resources == ['secrets']) &&
        object.rules.exists(r,
          has(r.verbs) &&
          r.verbs != null &&
          r.verbs == ['create'] &&
          (!has(r.resourceNames) || r.resourceNames == null || r.resourceNames.size() == 0)
        ) &&
        object.rules.exists(r,
          has(r.verbs) &&
          r.verbs != null &&
          r.verbs.size() > 0 &&
          r.verbs.all(v, v in ['delete', 'get', 'patch', 'update']) &&
          has(r.resourceNames) &&
          r.resourceNames != null &&
          r.resourceNames.size() > 0
        )
      )
    message: The Provisioner can only create the secrets writer Role with create +
      name-scoped mutations (resourceNames) on Secrets.
  - expression: |-
      !variables.is_provisioner || request.operation != 'DELETE' || request.resource.resource != 'roles' || (

        oldObject != null &&
        has(oldObject.metadata) &&
        oldObject.metadata.name in [
          'openbao-operator-tenant-role',
          'openbao-operator-tenant-secrets-reader',
          'openbao-operator-tenant-secrets-writer'
        ]
      )
    message: The Provisioner can only delete Roles for the operator tenant template
      (tenant + secrets allowlists).
  - expression: |-
      !variables.is_provisioner || request.operation != 'DELETE' || request.resource.resource != 'rolebindings' || (

        oldObject != null &&
        has(oldObject.metadata) &&
        oldObject.metadata.name in [
          'openbao-operator-tenant-rolebinding',
          'openbao-operator-tenant-secrets-reader-rolebinding',
          'openbao-operator-tenant-secrets-writer-rolebinding'
        ]
      )
    message: The Provisioner can only delete RoleBindings for the operator tenant
      template (tenant + secrets allowlists).
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-controller'''
    name: controller_serviceaccount_name
  - expression: '''openbao-operator-provisioner'''
    name: provisioner_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.provisioner_serviceaccount_name'
    name: provisioner_principal
  - expression: request.userInfo.username == variables.provisioner_principal
    name: is_provisioner
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-restrict-provisioner-tenant-governance
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - resourcequotas
      - limitranges
  validations:
  - expression: '!variables.is_provisioner || !variables.is_system_namespace'
    message: The Provisioner may not manage tenant ResourceQuota or LimitRange guardrails
      in system namespaces.
  - expression: '!variables.is_provisioner || variables.is_guardrail_request'
    message: The Provisioner can only manage the operator tenant ResourceQuota and
      LimitRange guardrail objects.
  - expression: '!variables.is_provisioner || variables.has_required_labels'
    message: The Provisioner must label tenant ResourceQuota and LimitRange guardrails
      as operator-managed.
  - expression: |-
      !variables.is_guardrail_request || variables.is_provisioner || (

        request.operation == 'DELETE' &&
        (
          variables.is_kube_controller_manager ||
          variables.is_system_controller ||
          variables.is_garbage_collector_sa ||
          variables.is_kube_system_controller
        )
      )
    message: Direct modification of the operator-managed tenant ResourceQuota and
      LimitRange guardrails is prohibited; update the parent OpenBaoTenant instead.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
  - expression: '''openbao-operator-provisioner'''
    name: provisioner_serviceaccount_name
  - expression: '''system:serviceaccount:'' + variables.operator_namespace + '':''
      + variables.provisioner_serviceaccount_name'
    name: provisioner_principal
  - expression: request.userInfo.username == variables.provisioner_principal
    name: is_provisioner
  - expression: request.userInfo.username == "system:kube-controller-manager" || request.userInfo.username
      == "system:serviceaccount:kube-system:kube-controller-manager"
    name: is_kube_controller_manager
  - expression: request.userInfo.username.startsWith("system:controller:")
    name: is_system_controller
  - expression: |-
      request.userInfo.username in [

        "system:serviceaccount:kube-system:generic-garbage-collector",
        "system:serviceaccount:kube-system:resourcequota-controller"
      ]
    name: is_garbage_collector_sa
  - expression: request.userInfo.groups.exists(g, g == "system:serviceaccounts:kube-system")
      || request.userInfo.username.startsWith("system:serviceaccount:kube-system:")
    name: is_kube_system_controller
  - expression: request.namespace == variables.operator_namespace || request.namespace.startsWith('kube-')
      || request.namespace == 'openshift' || request.namespace.startsWith('openshift-')
      || request.namespace.startsWith('gke-') || request.namespace.startsWith('eks-')
      || request.namespace.startsWith('aws-') || request.namespace.startsWith('aks-')
      || request.namespace.startsWith('azure-')
    name: is_system_namespace
  - expression: |-
      request.operation == 'DELETE' ?

        request.name :
        (
          object != null &&
          has(object.metadata) &&
          object.metadata.name != ''
        ) ? object.metadata.name : request.name
    name: resource_name
  - expression: |-
      (request.resource.resource == 'resourcequotas' &&

        variables.resource_name == 'openbao-operator-tenant-quota') ||
      (request.resource.resource == 'limitranges' &&

        variables.resource_name == 'openbao-operator-tenant-limits')
    name: is_guardrail_request
  - expression: |-
      request.operation == 'DELETE' || (

        object != null &&
        has(object.metadata) &&
        has(object.metadata.labels) &&
        ("app.kubernetes.io/name" in object.metadata.labels) &&
        object.metadata.labels["app.kubernetes.io/name"] == "openbao-operator" &&
        ("app.kubernetes.io/component" in object.metadata.labels) &&
        object.metadata.labels["app.kubernetes.io/component"] == "provisioner" &&
        ("app.kubernetes.io/managed-by" in object.metadata.labels) &&
        object.metadata.labels["app.kubernetes.io/managed-by"] == "openbao-operator"
      )
    name: has_required_labels
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-validate-openbao-tenant
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - openbao.org
      apiVersions:
      - v1alpha1
      operations:
      - CREATE
      - UPDATE
      resources:
      - openbaotenants
  validations:
  - expression: oldObject == null || object.spec.targetNamespace == oldObject.spec.targetNamespace
    message: spec.targetNamespace is immutable.
  - expression: object.metadata.namespace == variables.operator_namespace || !has(object.spec.targetNamespace)
      || object.spec.targetNamespace == '' || object.spec.targetNamespace == object.metadata.namespace
    message: OpenBaoTenant can only target its own namespace (self-service mode).
      Create the OpenBaoTenant in the operator namespace for cross-namespace targeting.
  - expression: |-
      object.metadata.namespace == variables.operator_namespace || (

        !has(object.spec.quota) &&
        !has(object.spec.limitRange)
      )
    message: Self-service OpenBaoTenant requests may not customize spec.quota or spec.limitRange.
      Create the OpenBaoTenant in the operator namespace for centrally managed tenant
      guardrail overrides.
  variables:
  - expression: '''openbao-operator-system'''
    name: operator_namespace
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-validate-openbaocluster
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - openbao.org
      apiVersions:
      - v1alpha1
      operations:
      - CREATE
      - UPDATE
      resources:
      - openbaoclusters
  validations:
  - expression: has(object.spec.profile)
    message: spec.profile is required and must be explicitly set to Hardened or Development.
  - expression: |-
      !has(object.spec.profile) || object.spec.profile != "Hardened" || (object.spec.tls.mode in ["External", "ACME"] &&

        has(object.spec.unseal) &&
        object.spec.unseal.type != "static" &&
        has(object.spec.selfInit) &&
        object.spec.selfInit.enabled == true &&
        !(has(object.spec.unseal.transit) && has(object.spec.unseal.transit.tlsSkipVerify) && object.spec.unseal.transit.tlsSkipVerify == true))
    message: Hardened profile requires TLS mode External or ACME, an external unseal
      (non-static), self-init enabled, and disallows tlsSkipVerify=true in seal configuration.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.unseal) || !has(object.spec.unseal.transit) || !has(object.spec.unseal.transit.token)
      || size(object.spec.unseal.transit.token) == 0'
    message: Hardened profile does not allow spec.unseal.transit.token; use spec.unseal.credentialsSecretRef
      instead.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.unseal) || !has(object.spec.unseal.transit) || !has(object.spec.unseal.transit.address)
      || object.spec.unseal.transit.address.startsWith("https://")'
    message: Hardened profile requires spec.unseal.transit.address to use HTTPS.
  - expression: |-
      !has(object.spec.unseal) || !has(object.spec.unseal.transit) || ((has(object.spec.unseal.transit.tlsClientCert) && size(object.spec.unseal.transit.tlsClientCert) > 0) ==

        (has(object.spec.unseal.transit.tlsClientKey) && size(object.spec.unseal.transit.tlsClientKey) > 0))
    message: spec.unseal.transit.tlsClientCert and spec.unseal.transit.tlsClientKey
      must be set together.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.imageVerification) || object.spec.imageVerification.enabled
      == true'
    message: Hardened profile does not allow spec.imageVerification.enabled=false.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.operatorImageVerification) || object.spec.operatorImageVerification.enabled
      == true'
    message: Hardened profile does not allow spec.operatorImageVerification.enabled=false.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.imageVerification) || !has(object.spec.imageVerification.failurePolicy)
      || object.spec.imageVerification.failurePolicy != "Warn"'
    message: Hardened profile does not allow spec.imageVerification.failurePolicy=Warn.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !has(object.spec.operatorImageVerification) || !has(object.spec.operatorImageVerification.failurePolicy)
      || object.spec.operatorImageVerification.failurePolicy != "Warn"'
    message: Hardened profile does not allow spec.operatorImageVerification.failurePolicy=Warn.
  - expression: '!has(object.spec.initContainer) || object.spec.initContainer.enabled
      == true'
    message: spec.initContainer is optional; when set, spec.initContainer.enabled
      must be true.
  - expression: '!has(object.spec.selfInit) || object.spec.selfInit.enabled == false
      || (has(object.spec.selfInit.requests) && size(object.spec.selfInit.requests)
      > 0)'
    message: spec.selfInit.requests is required and must not be empty when spec.selfInit.enabled
      is true.
  - expression: |-
      !has(object.spec.selfInit) || object.spec.selfInit.enabled == false || (!has(object.spec.selfInit.requests) ||

        (size(object.spec.selfInit.requests) <= 64 &&
          object.spec.selfInit.requests.all(r,
            object.spec.selfInit.requests.filter(x, x.name == r.name).size() == 1) &&
          object.spec.selfInit.requests.all(r, size(r.path) <= 256)))
    message: spec.selfInit.requests must have unique names (max 64) and request paths
      must be <= 256 characters.
  - expression: '!has(object.spec.selfInit) || !has(object.spec.selfInit.oidc) ||
      object.spec.selfInit.oidc.enabled == false || object.spec.selfInit.enabled ==
      true'
    message: spec.selfInit.oidc.enabled requires spec.selfInit.enabled to be true.
  - expression: '!has(object.spec.gateway) || object.spec.gateway.enabled == false
      || (object.spec.gateway.hostname != "" && object.spec.gateway.gatewayRef.name
      != "")'
    message: spec.gateway.hostname and spec.gateway.gatewayRef.name are required when
      Gateway is enabled.
  - expression: '!(variables.has_pre_upgrade_snapshot || variables.has_bluegreen_pre_upgrade_snapshot)
      || variables.has_backup'
    message: spec.backup is required when pre-upgrade snapshots are enabled (spec.upgrade.preUpgradeSnapshot
      or spec.upgrade.blueGreen.preUpgradeSnapshot).
  - expression: |-
      !variables.has_backup || (object.spec.backup.schedule != "" &&

        ((has(object.spec.backup.jwtAuthRole) && object.spec.backup.jwtAuthRole != "") || has(object.spec.backup.tokenSecretRef) || ((!has(object.spec.backup.jwtAuthRole) || object.spec.backup.jwtAuthRole == "") && has(object.spec.selfInit) && object.spec.selfInit.enabled == true && has(object.spec.selfInit.oidc) && object.spec.selfInit.oidc.enabled == true)) &&
        object.spec.backup.schedule.matches("^\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s+\\S+$"))
    message: spec.backup requires schedule, OpenBao auth (jwtAuthRole or tokenSecretRef,
      or self-init OIDC bootstrap enabled when jwtAuthRole is empty/unset for auto-creation
      of "openbao-operator-backup" role), and a 5-field cron expression. Storage credentials
      may come from target.credentialsSecretRef, target.roleArn, or the pod's ambient
      workload identity/default credential chain.
  - expression: oldObject == null || variables.requested_upgrade_strategy == variables.previous_upgrade_strategy
    message: spec.upgrade.strategy is immutable after creation; switching between
      RollingUpdate and BlueGreen is not supported.
  - expression: |-
      !has(object.spec.profile) || object.spec.profile != "Hardened" || !(variables.has_backup || variables.has_pre_upgrade_snapshot || variables.has_bluegreen_pre_upgrade_snapshot) || (has(object.spec.network) &&

        has(object.spec.network.egressRules) &&
        size(object.spec.network.egressRules) > 0)
    message: Hardened profile requires non-empty spec.network.egressRules when backups
      or pre-upgrade snapshots are enabled.
  - expression: |-
      !variables.has_backup || !has(object.spec.backup.target.endpoint) || object.spec.backup.target.endpoint == "" || !(

        object.spec.backup.target.endpoint.contains("localhost") ||
        object.spec.backup.target.endpoint.contains("127.0.0.1") ||
        object.spec.backup.target.endpoint.contains("::1") ||
        object.spec.backup.target.endpoint.matches("^https?://[0-9]+[:/].*")
      )
    message: Backup endpoint cannot point to localhost or use numeric IP encoding
      (SSRF protection).
  - expression: '!variables.has_backup || !has(object.spec.backup.target.endpoint)
      || object.spec.backup.target.endpoint == "" || !object.spec.backup.target.endpoint.matches("^https?://169\\.254\\..*")'
    message: Backup endpoint cannot point to link-local addresses (SSRF protection
      against cloud metadata services).
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      !variables.has_backup || !has(object.spec.backup.target.endpoint) || object.spec.backup.target.endpoint
      == "" || object.spec.backup.target.endpoint.startsWith("https://") || object.spec.backup.target.endpoint.startsWith("s3://")'
    message: Backup endpoint must use HTTPS or S3 scheme in Hardened profile.
  - expression: '!has(object.spec.profile) || object.spec.profile != "Hardened" ||
      object.spec.replicas >= 3'
    message: Hardened profile requires at least 3 replicas for Raft quorum HA. Use
      Profile=Development for non-HA deployments.
  - expression: |-
      (!has(object.spec.backup) || !has(object.spec.backup.target.credentialsSecretRef) ||

        !object.spec.backup.target.credentialsSecretRef.name.matches("^.*-(unseal-key|root-token|tls-ca|tls-server)$")) &&
      (!has(object.spec.backup) || !has(object.spec.backup.tokenSecretRef) ||

        !object.spec.backup.tokenSecretRef.name.matches("^.*-(unseal-key|root-token|tls-ca|tls-server)$"))
    message: References to system secrets (unseal-key, root-token, tls-ca, tls-server)
      are prohibited in backup configurations to prevent confused deputy attacks.
  - expression: 'oldObject == null || (has(object.spec.storage.storageClassName) ?
      object.spec.storage.storageClassName : "") == (has(oldObject.spec.storage.storageClassName)
      ? oldObject.spec.storage.storageClassName : "")'
    message: spec.storage.storageClassName is immutable after creation; set it explicitly
      before PVCs are created.
  - expression: oldObject == null || !has(oldObject.spec.storage.size) || quantity(object.spec.storage.size).compareTo(quantity(oldObject.spec.storage.size))
      >= 0
    message: spec.storage.size cannot be decreased.
  - expression: |-
      !has(object.spec.selfInit) || !has(object.spec.selfInit.oidc) || !has(object.spec.selfInit.oidc.issuer) || object.spec.selfInit.oidc.issuer == "" || (

        object.spec.selfInit.oidc.issuer.startsWith("https://") &&
        !object.spec.selfInit.oidc.issuer.contains("@") &&
        !object.spec.selfInit.oidc.issuer.contains("?") &&
        !object.spec.selfInit.oidc.issuer.contains("#") &&
        !object.spec.selfInit.oidc.issuer.contains("localhost") &&
        !object.spec.selfInit.oidc.issuer.contains("127.0.0.1") &&
        !object.spec.selfInit.oidc.issuer.contains("::1") &&
        !object.spec.selfInit.oidc.issuer.matches("^https?://169\\.254\\..*") &&
        !object.spec.selfInit.oidc.issuer.contains("/.well-known/")
      )
    message: spec.selfInit.oidc.issuer must be an https:// issuer URL (no userinfo/query/fragment;
      not localhost/link-local; must not include /.well-known/*).
  - expression: |-
      !has(object.spec.selfInit) || !has(object.spec.selfInit.requests) || size(object.spec.selfInit.requests) == 0 || !(

        object.spec.selfInit.requests.filter(r, r.path.startsWith("auth/jwt/") || r.path == "auth/jwt/config").size() > 0
      ) || object.spec.selfInit.requests.filter(r, r.path == "sys/auth/jwt").size() > 0
    message: spec.selfInit.requests contains auth/jwt/* paths but is missing a sys/auth/jwt
      request to enable that auth mount.
  - expression: |-
      !has(object.spec.selfInit) || !has(object.spec.selfInit.requests) || size(object.spec.selfInit.requests) == 0 || !(

        object.spec.selfInit.requests.filter(r, r.path.startsWith("auth/jwt-operator/") || r.path == "auth/jwt-operator/config").size() > 0
      ) || (has(object.spec.selfInit.oidc) && object.spec.selfInit.oidc.enabled == true) || object.spec.selfInit.requests.filter(r, r.path == "sys/auth/jwt-operator").size() > 0
    message: 'spec.selfInit.requests contains auth/jwt-operator/* paths but is missing
      sys/auth/jwt-operator (or spec.selfInit.oidc.enabled: true) to enable that auth
      mount.'
  - expression: '!variables.requested_below_current_version'
    message: spec.version cannot be downgraded below status.currentVersion.
  - expression: '!variables.requested_below_rolling_target'
    message: spec.version cannot be reduced below status.upgrade.targetVersion after
      rolling progress has started.
  variables:
  - expression: has(object.spec.backup)
    name: has_backup
  - expression: 'has(object.spec.upgrade) && has(object.spec.upgrade.strategy) &&
      object.spec.upgrade.strategy != "" ? object.spec.upgrade.strategy : "RollingUpdate"'
    name: requested_upgrade_strategy
  - expression: 'oldObject != null && has(oldObject.spec.upgrade) && has(oldObject.spec.upgrade.strategy)
      && oldObject.spec.upgrade.strategy != "" ? oldObject.spec.upgrade.strategy :
      "RollingUpdate"'
    name: previous_upgrade_strategy
  - expression: has(object.spec.upgrade) && has(object.spec.upgrade.preUpgradeSnapshot)
      && object.spec.upgrade.preUpgradeSnapshot == true
    name: has_pre_upgrade_snapshot
  - expression: has(object.spec.upgrade) && has(object.spec.upgrade.blueGreen) &&
      has(object.spec.upgrade.blueGreen.preUpgradeSnapshot) && object.spec.upgrade.blueGreen.preUpgradeSnapshot
      == true
    name: has_bluegreen_pre_upgrade_snapshot
  - expression: 'object.spec.version.startsWith("v") ? object.spec.version.substring(1)
      : object.spec.version'
    name: spec_version_normalized
  - expression: 'variables.spec_version_normalized.contains("+") ? variables.spec_version_normalized.substring(0,
      variables.spec_version_normalized.indexOf("+")) : variables.spec_version_normalized'
    name: spec_version_build_stripped
  - expression: 'variables.spec_version_build_stripped.contains("-") ? variables.spec_version_build_stripped.substring(0,
      variables.spec_version_build_stripped.indexOf("-")) : variables.spec_version_build_stripped'
    name: spec_version_core
  - expression: 'variables.spec_version_build_stripped.contains("-") ? variables.spec_version_build_stripped.substring(variables.spec_version_build_stripped.indexOf("-")
      + 1) : ""'
    name: spec_version_prerelease
  - expression: variables.spec_version_normalized.matches("^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$")
    name: spec_version_valid
  - expression: variables.spec_version_core.split(".").map(part, int(part))
    name: spec_version_parts
  - expression: 'oldObject != null && has(oldObject.status) && has(oldObject.status.currentVersion)
      && oldObject.status.currentVersion != "" ? (oldObject.status.currentVersion.startsWith("v")
      ? oldObject.status.currentVersion.substring(1) : oldObject.status.currentVersion)
      : ""'
    name: current_version_normalized
  - expression: 'variables.current_version_normalized.contains("+") ? variables.current_version_normalized.substring(0,
      variables.current_version_normalized.indexOf("+")) : variables.current_version_normalized'
    name: current_version_build_stripped
  - expression: 'variables.current_version_build_stripped.contains("-") ? variables.current_version_build_stripped.substring(0,
      variables.current_version_build_stripped.indexOf("-")) : variables.current_version_build_stripped'
    name: current_version_core
  - expression: 'variables.current_version_build_stripped.contains("-") ? variables.current_version_build_stripped.substring(variables.current_version_build_stripped.indexOf("-")
      + 1) : ""'
    name: current_version_prerelease
  - expression: variables.current_version_normalized.matches("^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$")
    name: current_version_valid
  - expression: variables.current_version_core.split(".").map(part, int(part))
    name: current_version_parts
  - expression: 'oldObject != null && has(oldObject.status) && has(oldObject.status.upgrade)
      && has(oldObject.status.upgrade.targetVersion) && oldObject.status.upgrade.targetVersion
      != "" ? (oldObject.status.upgrade.targetVersion.startsWith("v") ? oldObject.status.upgrade.targetVersion.substring(1)
      : oldObject.status.upgrade.targetVersion) : ""'
    name: rolling_target_version_normalized
  - expression: 'variables.rolling_target_version_normalized.contains("+") ? variables.rolling_target_version_normalized.substring(0,
      variables.rolling_target_version_normalized.indexOf("+")) : variables.rolling_target_version_normalized'
    name: rolling_target_version_build_stripped
  - expression: 'variables.rolling_target_version_build_stripped.contains("-") ? variables.rolling_target_version_build_stripped.substring(0,
      variables.rolling_target_version_build_stripped.indexOf("-")) : variables.rolling_target_version_build_stripped'
    name: rolling_target_version_core
  - expression: 'variables.rolling_target_version_build_stripped.contains("-") ? variables.rolling_target_version_build_stripped.substring(variables.rolling_target_version_build_stripped.indexOf("-")
      + 1) : ""'
    name: rolling_target_version_prerelease
  - expression: variables.rolling_target_version_normalized.matches("^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$")
    name: rolling_target_version_valid
  - expression: variables.rolling_target_version_core.split(".").map(part, int(part))
    name: rolling_target_version_parts
  - expression: oldObject != null && has(oldObject.status) && has(oldObject.status.upgrade)
      && (oldObject.status.upgrade.currentPartition < oldObject.spec.replicas || (has(oldObject.status.upgrade.completedPods)
      && size(oldObject.status.upgrade.completedPods) > 0))
    name: rolling_upgrade_progress_started
  - expression: |-
      variables.current_version_valid && variables.spec_version_valid && (

        variables.spec_version_parts[0] < variables.current_version_parts[0] ||
        (variables.spec_version_parts[0] == variables.current_version_parts[0] &&
          (
            variables.spec_version_parts[1] < variables.current_version_parts[1] ||
            (variables.spec_version_parts[1] == variables.current_version_parts[1] &&
              (
                variables.spec_version_parts[2] < variables.current_version_parts[2] ||
                (variables.spec_version_parts[2] == variables.current_version_parts[2] &&
                  (
                    (variables.current_version_prerelease == "" && variables.spec_version_prerelease != "") ||
                    (variables.current_version_prerelease != "" &&
                      variables.spec_version_prerelease != "" &&
                      variables.spec_version_prerelease < variables.current_version_prerelease)
                  )
                )
              )
            )
          )
        )
      )
    name: requested_below_current_version
  - expression: |-
      variables.rolling_upgrade_progress_started && variables.rolling_target_version_valid && variables.spec_version_valid && (

        variables.spec_version_parts[0] < variables.rolling_target_version_parts[0] ||
        (variables.spec_version_parts[0] == variables.rolling_target_version_parts[0] &&
          (
            variables.spec_version_parts[1] < variables.rolling_target_version_parts[1] ||
            (variables.spec_version_parts[1] == variables.rolling_target_version_parts[1] &&
              (
                variables.spec_version_parts[2] < variables.rolling_target_version_parts[2] ||
                (variables.spec_version_parts[2] == variables.rolling_target_version_parts[2] &&
                  (
                    (variables.rolling_target_version_prerelease == "" && variables.spec_version_prerelease != "") ||
                    (variables.rolling_target_version_prerelease != "" &&
                      variables.spec_version_prerelease != "" &&
                      variables.spec_version_prerelease < variables.rolling_target_version_prerelease)
                  )
                )
              )
            )
          )
        )
      )
    name: requested_below_rolling_target
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: openbao-operator-openbao-validate-openbaorestore
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups:
      - openbao.org
      apiVersions:
      - v1alpha1
      operations:
      - CREATE
      - UPDATE
      resources:
      - openbaorestores
  validations:
  - expression: oldObject == null || object.spec == oldObject.spec
    message: spec is immutable; create a new OpenBaoRestore for changed restore parameters.
  - expression: '!has(object.spec.overrideOperationLock) || object.spec.overrideOperationLock
      == false || object.spec.force == true'
    message: spec.overrideOperationLock requires spec.force=true.
  - expression: |-
      !has(object.spec.source.target.endpoint) || object.spec.source.target.endpoint == "" || !(

        object.spec.source.target.endpoint.contains("localhost") ||
        object.spec.source.target.endpoint.contains("127.0.0.1") ||
        object.spec.source.target.endpoint.contains("::1") ||
        object.spec.source.target.endpoint.matches("^https?://[0-9]+[:/].*")
      )
    message: Restore endpoint cannot point to localhost or use numeric IP encoding
      (SSRF protection).
  - expression: '!has(object.spec.source.target.endpoint) || object.spec.source.target.endpoint
      == "" || !object.spec.source.target.endpoint.matches("^https?://169\\.254\\..*")'
    message: Restore endpoint cannot point to link-local addresses (SSRF protection
      against cloud metadata services).
  - expression: '!has(object.spec.source.target.endpoint) || object.spec.source.target.endpoint
      == "" || object.spec.source.target.endpoint.startsWith("https://") || object.spec.source.target.endpoint.startsWith("s3://")
      || object.spec.source.target.endpoint.matches("^http://[^/]+\\.svc(\\.|:|/|$).*")'
    message: Restore endpoint must use HTTPS or S3 scheme, unless it targets an in-cluster
      Service (*.svc).
  - expression: |-
      (!has(object.spec.source.target.credentialsSecretRef) ||

        !object.spec.source.target.credentialsSecretRef.name.matches("^.*-(unseal-key|root-token|tls-ca|tls-server)$")) &&
      (!has(object.spec.tokenSecretRef) ||

        !object.spec.tokenSecretRef.name.matches("^.*-(unseal-key|root-token|tls-ca|tls-server)$"))
    message: References to system secrets (unseal-key, root-token, tls-ca, tls-server)
      are prohibited in restore configuration to prevent confused deputy attacks.
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-enforce-managed-image-digests-binding
spec:
  policyName: openbao-operator-openbao-enforce-managed-image-digests
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-lock-controller-statefulset-mutations-binding
spec:
  policyName: openbao-operator-openbao-lock-controller-statefulset-mutations
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-lock-managed-resource-mutations-binding
spec:
  policyName: openbao-operator-openbao-lock-managed-resource-mutations
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-controller-rbac-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - rbac.authorization.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - roles
      - rolebindings
  policyName: openbao-operator-openbao-restrict-controller-rbac
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-controller-secret-writes-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - secrets
  policyName: openbao-operator-openbao-restrict-controller-secret-writes
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-controller-serviceaccounts-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - serviceaccounts
  policyName: openbao-operator-openbao-restrict-controller-serviceaccounts
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-provisioner-namespace-mutations-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - UPDATE
      resources:
      - namespaces
  policyName: openbao-operator-openbao-restrict-provisioner-namespace-mutations
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-provisioner-rbac-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - rbac.authorization.k8s.io
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - roles
      - rolebindings
  policyName: openbao-operator-openbao-restrict-provisioner-rbac
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-restrict-provisioner-tenant-governance-binding
spec:
  matchResources:
    resourceRules:
    - apiGroups:
      - ""
      apiVersions:
      - v1
      operations:
      - CREATE
      - UPDATE
      - DELETE
      resources:
      - resourcequotas
      - limitranges
  policyName: openbao-operator-openbao-restrict-provisioner-tenant-governance
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-validate-openbao-tenant-binding
spec:
  policyName: openbao-operator-openbao-validate-openbao-tenant
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-validate-openbaocluster-binding
spec:
  policyName: openbao-operator-openbao-validate-openbaocluster
  validationActions:
  - Deny
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: openbao-operator-openbao-validate-openbaorestore-binding
spec:
  policyName: openbao-operator-openbao-validate-openbaorestore
  validationActions:
  - Deny
