## Secrets Manager External Secrets Operator integrates with [OVHcloud KMS](https://www.ovhcloud.com/en/identity-security-operations/key-management-service/). This guide demonstrates: - how to set up a `ClusterSecretStore`/`SecretStore` with the OVH provider. - `ExternalSecret` use cases with examples. - `PushSecret` use cases with examples. This guide assumes: - External Secrets Operator is already installed - You have access to OVHcloud Secret Manager - Required credentials are already created ### SecretStore **OVH provider supports both `token` and `mTLS` authentication.** Token authentication: ```yaml apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: secret-store-ovh namespace: default spec: provider: ovh: server: okmsid: auth: token: tokenSecretRef: name: ovh-token key: token --- apiVersion: v1 kind: Secret metadata: name: ovh-token data: token: BASE64-TOKEN-VALUE-PLACEHOLDER ``` mTLS authentication: ```yaml apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: secret-store-ovh namespace: default spec: provider: ovh: server: "https://eu-west-rbx.okms.ovh.net" okmsid: "734b9b45-8b1a-469c-b140-b10bd6540017" auth: mtls: certSecretRef: name: ovh-mtls key: tls.crt keySecretRef: name: ovh-mtls key: tls.key --- apiVersion: v1 kind: Secret metadata: name: ovh-mtls namespace: default type: kubernetes.io/tls data: tls.crt: BASE64_CERT_PLACEHOLDER # "client certificate value" tls.key: BASE64_KEY_PLACEHOLDER # "client key value" ``` !!! note A `ClusterSecretStore` configuration is the same except you must provide the `namespace` for `tokenSecretRef`, `certSecretRef` and `keySecretRef` according to your chosen authentication method. ### ExternalSecret For these examples, we will assume you have the following secret in your Secret Manager: ```json { "path": "creds", "data": { "type": "credential", "users": { "kevin": { "token": "kevin token value" }, "laura": { "token": "laura token value" } } } } ``` `path` refers to the secret's path in OVH Secret Manager. ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example data: - secretKey: foo remoteRef: key: creds version: version property: property ``` | Field | Description | Required | |------------|------------------------------------------------------------------------|----------| | version | Secret version to retrieve | No | | property | Specific key or nested key in the secret | No | | secretKey | The key inside the Kubernetes Secret that will hold the secret's value | Yes | #### Fetch the whole secret - Using `spec.data` ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example data: - secretKey: foo remoteRef: key: creds ``` Resulting Kubernetes Secret data: ```json { "foo": { "type": "credential", "users": { "kevin": { "token": "kevin token value" }, "laura": { "token": "laura token value" } } } } ``` - Using `spec.dataFrom.extract` ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example dataFrom: - extract: key: creds ``` Resulting Kubernetes Secret data: ```json { "type": "credential", "users": { "kevin": { "token": "kevin token value" }, "laura": { "token": "laura token value" } } } ``` #### Fetch scalar/nested values - Scalar value using `data` ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example data: - secretKey: type remoteRef: key: creds property: type ``` Resulting Kubernetes Secret data: ```json { "type": "credential" } ``` - Nested value using `data` ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example data: - secretKey: kevin-token remoteRef: key: creds property: users.kevin.token ``` Resulting Kubernetes Secret data: ```json { "kevin-token": "kevin token value" } ``` - Nested value using `dataFrom.extract` ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example dataFrom: - extract: key: creds property: users ``` Resulting Kubernetes Secret data: ```json { "kevin": { "token": "kevin token value" }, "laura": { "token": "laura token value" } } ``` !!! warning Scalar values cannot be retrieved using `dataFrom.extract` because no Kubernetes secret key can be specified, which would imply storing a value without a corresponding key. #### Fetch multiple secrets Extract multiple secrets, with filtering support. You can filter either by path or/and regular expression. Path filtering occurs first if you use both. For these examples, we will assume you have the following secrets in your Secret Manager: `path/to/secret/secret1`, `path/to/secret/secret2`, `path/to/config/config2`, `path/to/config/config3`, `secret-example2`. - Path filtering ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example dataFrom: - find: path: "path/to/secret" ``` Resulting Kubernetes Secret data: ```json { "path/to/secret/secret1": "secret1 value", "path/to/secret/secret2": "secret2 value" } ``` !!! note If path is left empty or is "/", every secret will be retrieved from your Secret Manager. - Regular expression filtering ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example dataFrom: - find: name: regexp: "[2-3]" ``` Resulting Kubernetes Secret data: ```json { "path/to/secret/secret2": "secret2 value", "path/to/config/config2": "config2 value", "path/to/config/config3": "config3 value", "secret-example2": "secret-example2 value" } ``` !!! note If name.regexp is left empty, every secret will be retrieved from your Secret Manager. - Combination of both ```yaml apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-ovh namespace: default spec: secretStoreRef: name: secret-store-ovh kind: SecretStore target: name: secret-example dataFrom: - find: path: "path/to" name: regexp: "2$" ``` Resulting Kubernetes Secret data: ```json { "path/to/secret/secret2": "secret2 value", "path/to/config/config2": "config2 value" } ``` !!! note When both are combined, path filtering occurs first. ### PushSecret #### Check-And-Set Check-And-Set can be enabled/disabled (default: disabled), in the Secret Store configuration: ```yaml apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: secret-store-ovh namespace: default spec: provider: ovh: server: okmsid: auth: token: tokenSecretRef: name: ovh-token key: token casRequired: true --- apiVersion: v1 kind: Secret metadata: name: ovh-token data: token: BASE64_TOKEN_PLACEHOLDER # "token value" ``` #### Secret Rotation ```yaml apiVersion: generators.external-secrets.io/v1alpha1 kind: Password metadata: name: my-password-generator spec: length: 32 digits: 5 symbols: 5 symbolCharacters: "-_^$%*รน/;:,?" noUpper: false allowRepeat: true --- apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: push-secret-ovh spec: refreshInterval: 6h0m0s secretStoreRefs: - name: secret-store-ovh kind: SecretStore selector: generatorRef: apiVersion: generators.external-secrets.io/v1alpha1 kind: Password name: my-password-generator data: - match: secretKey: password # property in the generator output remoteRef: remoteKey: prod/mysql/password ``` With this configuration, the secret is automatically rotated every 6 hours in the OVH Secret Manager. #### Secret migration ```yaml apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: secret-store-vault namespace: default spec: provider: vault: server: "https://my.vault.server:8200" path: "secret" version: "v2" auth: tokenSecretRef: name: vault-token key: token --- apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: external-secret-vault namespace: default spec: secretStoreRef: name: secret-store-vault kind: SecretStore refreshPolicy: Periodic refreshInterval: "10s" target: name: creds-secret-vault dataFrom: - extract: key: example --- apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: secret-store-ovh namespace: default spec: provider: ovh: server: okmsid: auth: token: tokenSecretRef: name: ovh-token key: token --- apiVersion: external-secrets.io/v1alpha1 kind: PushSecret metadata: name: push-secret-ovh spec: secretStoreRefs: - name: secret-store-ovh kind: SecretStore selector: secret: name: creds-secret-vault refreshInterval: 10s data: - match: secretKey: "secretKey" remoteRef: remoteKey: "creds-secret-migrated" ``` This example demonstrates how to fetch a secret from a HashiCorp Vault KV secrets engine and sync it into OVH Secret Manager.