Kubernetes
August 15, 2023

Установка ExternalSecret Operator + LockBox

ExternalSecret позволяет использовать внешнюю, по отношению к кластеру Kubernetes, секретницу. В YC этой секретницей является Lockbox.

Предполагаю, что SA есть и IAM ключ для него уже сгенерирован. Если нет, то можно воспользоваться инструкцией: https://teletype.in/@cameda/w93lPMPQWS4

На всякий случай. Генерацию IAM ключа для SA можно выполнить так:

yc iam key create --service-account-id $SA --output key.json

Создаётся файл key.json, который можно использовать в дальнейшем.

KMS ключ сгенерировал здесь: https://teletype.in/@cameda/vBIMDOZi7vV

Lockbox был создан по этому мануалу: https://teletype.in/@cameda/M1Rm88sCB7t

Установим ExternalSecret Operator с помощью Helm.

export HELM_EXPERIMENTAL_OCI=1 && \
helm pull oci://cr.yandex/yc-marketplace/yandex-cloud/external-secrets/chart/external-secrets \
  --version 0.5.5 \
  --untar && \
helm install \
  --namespace external-secret \
  --create-namespace \
  --set-file auth.json=key.json \
  external-secrets ./external-secrets/
helm list -n external-secret
NAME            	NAMESPACE      	REVISION	UPDATED                             	STATUS  	CHART                 	APP VERSION
external-secrets	external-secret	1       	2023-08-15 14:04:37.243386 +0300 MSK	deployed	external-secrets-0.5.5	v0.5.5

Создаём секрет на основе файла key.json.

kubectl create secret generic yc-auth --from-file=authorized-key=key.json
# Проверяем, что содержимое соответствует файлу.
kubectl get secret yc-auth --output=json | jq --raw-output ".data[]" | base64 -d

Создаём SecretStore на основе секрета.

kubectl apply -f - <<< '
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secret-store
spec:
  provider:
    yandexlockbox:
      auth:
        authorizedKeySecretRef:
          name: yc-auth
          key: authorized-key'
kubectl get ss
NAME           AGE   STATUS
secret-store   4s    Valid

SecretStore в статусе Valid. Значит создалось нормально.

Создаём ExternalSecret.

kubectl apply -f - <<< '
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: external-secret
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: secret-store
    kind: SecretStore
  target:
    name: k8s-secret
  data:
  - secretKey: ssh
    remoteRef:
      key: e6qad2p5dog8d6e07mol
      property: ssh'

где secret-store - имя созданного SecretStore для обеспечения доступа к Lockbox,
k8s-secret - имя создаваемого секрета,
e6qa5j84t1gaq1fc1lgr - идентификатор секрета в Lockbox,
ssh - ключ секрета.

kubectl get es
NAME              STORE          REFRESH INTERVAL   STATUS
external-secret   secret-store   1h                 SecretSynced
# Проверяем, что содержимое ключа отдаётся верно.
kubectl get secret k8s-secret --output=json | jq --raw-output ".data.ssh" | base64 -d

ExternalSecret Operator создаёт несколько сущностей.

kubectl api-resources
clusterexternalsecrets            ces                 external-secrets.io/v1beta1            false        ClusterExternalSecret
clustersecretstores               css                 external-secrets.io/v1beta1            false        ClusterSecretStore
externalsecrets                   es                  external-secrets.io/v1beta1            true         ExternalSecret
secretstores                      ss                  external-secrets.io/v1beta1            true         SecretStore

Создаваемые ресурсы.

kubectl get po -n external-secret
NAME                                                READY   STATUS    RESTARTS   AGE
external-secrets-5b6fbc88f9-286m6                   1/1     Running   0          13m
external-secrets-cert-controller-7b7d4c9bcd-7gnq4   1/1     Running   0          13m
external-secrets-webhook-7446c9896-br9ql            1/1     Running   0          13m
kubectl get deploy -n external-secret
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
external-secrets                   1/1     1            1           31m
external-secrets-cert-controller   1/1     1            1           31m
external-secrets-webhook           1/1     1            1           31m
kubectl get svc -n external-secret
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
external-secrets-webhook   ClusterIP   10.75.239.144   <none>        443/TCP   17m
kubectl get crd
NAME                                             CREATED AT
clusterexternalsecrets.external-secrets.io       2023-08-16T09:50:37Z
clustersecretstores.external-secrets.io          2023-08-16T09:50:37Z
externalsecrets.external-secrets.io              2023-08-16T09:50:37Z
secretstores.external-secrets.io                 2023-08-16T09:50:37Z
kubectl get validatingwebhookconfigurations
NAME                      WEBHOOKS   AGE
externalsecret-validate   1          19m
secretstore-validate      2          19m
kubectl get secret -n external-secret
NAME                                     TYPE                 DATA   AGE
external-secrets-webhook                 Opaque               4      22m
sa-creds                                 Opaque               1      22m
sh.helm.release.v1.external-secrets.v1   helm.sh/release.v1   1      22m
kubectl get cm -n external-secret
NAME               DATA   AGE
kube-root-ca.crt   1      22m
kubectl get sa -n external-secret
NAME                               SECRETS   AGE
default                            0         23m
external-secrets                   0         23m
external-secrets-cert-controller   0         23m
external-secrets-webhook           0         23m
kubectl get role -n external-secret
NAME                              CREATED AT
external-secrets-leaderelection   2023-08-16T09:50:37Z

kubectl describe role external-secrets-leaderelection -n external-secret
Name:         external-secrets-leaderelection
Labels:       app.kubernetes.io/instance=external-secrets
              app.kubernetes.io/managed-by=Helm
              app.kubernetes.io/name=external-secrets
              app.kubernetes.io/version=v0.5.5
              helm.sh/chart=external-secrets-0.5.5
Annotations:  meta.helm.sh/release-name: external-secrets
              meta.helm.sh/release-namespace: external-secret
PolicyRule:
  Resources                   Non-Resource URLs  Resource Names                 Verbs
  ---------                   -----------------  --------------                 -----
  configmaps                  []                 []                             [create]
  leases.coordination.k8s.io  []                 []                             [get create update patch]
  configmaps                  []                 [external-secrets-controller]  [get update patch]

Установка из родного репозитория.

helm repo add external-secrets https://charts.external-secrets.io 
helm install external-secrets \
  external-secrets/external-secrets \
  -n external-secrets \
  --create-namespace

После этого создаём SecretStore и ExternalSecret.
Они успешно создаются.

kubectl get ss
NAME           AGE   STATUS   CAPABILITIES   READY
secret-store   22s   Valid    ReadOnly       True

kubectl get es
NAME              STORE          REFRESH INTERVAL   STATUS         READY
external-secret   secret-store   1h                 SecretSynced   True

Полезные ссылки.

https://external-secrets.io/latest/provider/yandex-lockbox/

Установка из стандартного helm:
https://external-secrets.io/latest/introduction/getting-started/

Использование совместно с LockBox:
https://external-secrets.io/latest/provider/yandex-lockbox/

Использование совместно с Certificate Manager:
https://external-secrets.io/latest/provider/yandex-certificate-manager/

Пример использования совместно с FluxCD:
https://external-secrets.io/latest/examples/gitops-using-fluxcd/