# KeyRotation declares one credential and how to rotate it. # # The resource never holds a secret value. It names where the value goes and how # to prove the new value works. The controller writes the Secret. apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: keyrotations.iam.homelab.riotpiao.com spec: group: iam.homelab.riotpiao.com scope: Namespaced names: kind: KeyRotation plural: keyrotations singular: keyrotation shortNames: [kr] versions: - name: v1 served: true storage: true additionalPrinterColumns: - {name: Provider, type: string, jsonPath: .spec.provider} - {name: Strategy, type: string, jsonPath: .spec.strategy} - {name: Phase, type: string, jsonPath: .status.phase} - {name: Last, type: date, jsonPath: .status.lastRotation} - {name: Next, type: date, jsonPath: .status.nextRotation} subresources: status: {} schema: openAPIV3Schema: type: object required: [spec] properties: spec: type: object required: [provider, strategy, intervalHours, target] properties: provider: type: string enum: [zitadel-machine-key, zitadel-oidc-client] description: >- Which external API creates the credential. strategy: type: string enum: [overlap, atomic] description: >- overlap keeps the old credential valid until the new one is proven. atomic replaces it in place, because the provider allows only one value. intervalHours: type: integer minimum: 1 description: How often to rotate. suspend: type: boolean description: Stop automatic rotation without deleting the resource. target: type: object required: [secretName, secretNamespace] properties: zitadelProjectID: {type: string} zitadelAppID: {type: string} machineUserID: {type: string} secretName: {type: string} secretNamespace: {type: string} secretKeys: type: object additionalProperties: {type: string} description: >- Logical field name to Secret key. Unmapped fields keep their logical name. reload: type: string enum: [hot, restart] description: >- hot means the consumer watches the file. restart rolls the consumer so it reads the new value at start. consumers: type: array items: type: object required: [kind, name, namespace] properties: kind: {type: string, enum: [Deployment, StatefulSet]} name: {type: string} namespace: {type: string} verify: type: object properties: introspect: type: boolean description: Check client id and secret against introspection. tokenExchange: type: boolean description: Exchange a token with the new machine key. audienceProject: type: string description: Project id added to the token audience scope. status: type: object properties: phase: type: string enum: [Pending, Preparing, Propagating, Verifying, Completing, Succeeded, Failed, Suspended] observedGeneration: {type: integer, format: int64} lastRotation: {type: string, format: date-time} nextRotation: {type: string, format: date-time} activeCredentialID: {type: string} previousCredentialID: {type: string} lastError: {type: string} conditions: type: array items: type: object required: [type, status, reason, lastTransitionTime] properties: type: {type: string} status: {type: string} reason: {type: string} message: {type: string} lastTransitionTime: {type: string, format: date-time}