Cuando comenzamos a desplegar en Kubernetes es común que nos preguntemos ¿dónde debemos alojar los secretos?, ¿cómo realizamos el despliegue de estos secretos en un flujo de Continuous Delivery (CD)?, ¿es posible versionar los cambios realizados en ellos? Estas cuestiones y algunas otras las vamos a aclarar en esta entrada.
En DataDope, para facilitar la administración, despliegue y versionado de los secretos utilizamos Sealed Secrets. Sealed Secrets es una herramienta desarrollada por Bitnami (VMWare).
Sealed Secrets utiliza cifrado asimétrico (clave pública y clave privada). Una vez desplegado el Sealed Secrets Controller, este generará un par de claves que utilizaremos para cifrar o descifrar nuestros secretos.
- Clave pública: Esta clave se utilizará para generar los Sealed Secrets a partir de un secreto. Esta debe ser descargada por cada uno de los usuarios de despliegue en el clúster de Kubernetes.
- Clave privada: Será la encargada de descifrar los secretos a partir de los Sealed Secrets cifrados previamente con la clave pública. Esta clave inicialmente sólo será utilizada por el Sealed Secret Controller. Debemos evitar descargar esa clave desde el controlador ya que podría comprometer todos los secretos cifrados.
El secreto cifrado (Sealed Secret) se podrá versionar en Git. Este procedimiento permite centralizar en un único repositorio todos los manifiestos que componen la entrega continua (CD) de nuestros recursos a Kubernetes, por ejemplo, haciendo usos de herramientas como ArgoCD o FluxCD.
Instalación
En primer lugar, es necesario desplegar Sealed Secrets Controller. Puedes encontrar todas las versiones disponibles en https://github.com/bitnami-labs/sealed-secrets/releases.
$ export SEALED_SECRETS_VERSION=0.18.0$ curl -sSLo controller.yaml «https://github.com/bitnami-labs/sealed-secrets/releases/download/v${SEALED_SECRETS_VERSION}/controller.yaml»
$ kubectl apply -f controller.yaml
clusterrolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
clusterrole.rbac.authorization.k8s.io/secrets-unsealer created
serviceaccount/sealed-secrets-controller created
rolebinding.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
role.rbac.authorization.k8s.io/sealed-secrets-key-admin created
role.rbac.authorization.k8s.io/sealed-secrets-service-proxier created
rolebinding.rbac.authorization.k8s.io/sealed-secrets-controller created
deployment.apps/sealed-secrets-controller created
customresourcedefinition.apiextensions.k8s.io/sealedsecrets.bitnami.com created
service/sealed-secrets-controller created
El siguiente paso sería instalar kubeseal. Es una herramienta usada mediante línea de comandos que nos permite cifrar y descifrar los Sealed Secrets.
$ curl -sSLo kubeseal-${SEALED_SECRETS_VERSION}-linux-amd64.tar.gz «https://github.com/bitnami-labs/sealed-secrets/releases/download/v${SEALED_SECRETS_VERSION}/kubeseal-${SEALED_SECRETS_VERSION}-linux-amd64.tar.gz»
$ tar xzf kubeseal-${SEALED_SECRETS_VERSION}-linux-amd64.tar.gz
$ cp kubeseal /usr/local/bin/kubeseal
Uso
Descargamos la clave pública que utilizaremos para cifrar los secretos.
$ kubeseal –fetch-cert –controller-name sealed-secrets-controller
> sealed-secrets.crt
Creamos el secreto a cifrar.
$ cat secret.yaml
apiVersion: v1
data:
password: YmFyCg==
kind: Secret
metadata:
annotations:
sealedsecrets.bitnami.com/managed: «true»
sealedsecrets.bitnami.com/namespace wide: «true»
name: foo
namespace: default
type: Opaque
- bitnami.com/managed: Con esta anotación le indicamos al controlador que puede sobreescribir en el nombre de espacio indicado el secreto si este ya existiera con el mismo nombre.
- bitnami.com/namespace-wide: El secreto puede ser libremente renombrado en el nombre de espacio definido en el secreto.
Ciframos el secreto con kubeseal utilizando la clave pública.
$ kubeseal –format yaml –cert sealed-secrets.crt <secret.yaml>
sealed-secret.yaml
Ejemplo de secreto cifrado:
$ cat sealed-secret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
annotations:
sealedsecrets.bitnami.com/namespace-wide: «true»
creationTimestamp: null
name: foo
namespace: default
spec:
encryptedData:
password: AgBT3O2pIbaeljVJIT4JK4Y+gzyo3dvJDdP2Cb75MirELjzk54Tj2Vq+rh33rdphtRFglJNmIgzXVGYctXAHaJes+lbCD+KDlpJJkn3N4EB97ypgD3e+14fet9nvOLnZxCgCMgDesp8NEX94ek/TgemmZIjpLQiCjKq41CosgQG+Bxk/lbODv1gj2fr0wvwvDrfZExEj4R4Rw5B1fUhzxgqDc1XAX/mfTOUAfyS2nC7iUyaFLh2JefYD42GtSnSF3PUadO5hY26hhkYy5JiG1M1EtX7b91HdkqBeU2jfdaC11gcHWOhoWyMWl0X6bz9JTFhirfOYW3T1uADv4ySk9No9vmmtk9CfPLe+/uWVzfv0KI93VmP9Azwi476OBIHXPZuB+nOZfA57om7mIQUEFBdaJl3a/hCdTm4Fr61Jl4GJ8CoS73nEzpef++fo0CETxxh7F+LGUH+re2roUlxBgL7lKvIocZjkbOilfEA51vIUwEishkpBM6OfbTZiyVroMi0XISAULzZgszJXeafIN/leLQk3gLHlzPrHLaXr5TAaXf0JMOpopY9ibeSuovGYnnFcaNdDoaIeS5AUPp5/kdmYk6qg3V6YXxd/c9NfAS76NKBbXCTFBcnArkz7/zpfWlFYjHG0B+xwortFWReHToMTZ4ZWC51aRaRa4J43NWlL3Oh9ImRYxpPrjB0ukWK/wghoTBTk
template:
data: null
metadata:
annotations:
sealedsecrets.bitnami.com/managed: «true»
sealedsecrets.bitnami.com/namespace-wide: «true»
creationTimestamp: null
name: foo
namespace: default
type: Opaque
Desplegamos el secreto cifrado en Kubernetes.
$ kubectl apply -f sealed-secret.yaml
sealedsecret.bitnami.com/foo created
Comprobamos que se ha creado el nuevo Sealed Secret…
$ kubectl get sealedsecrets
NAME AGE
foo 2m5s
Y su correspondiente secreto:
$ kubectl describe secret foo
Name: foo
Namespace: default
Labels: <none>
Annotations: sealedsecrets.bitnami.com/managed: true
sealedsecrets.bitnami.com/namespace-wide: true
Type: Opaque
Data
====
password: 4 bytes
Ya tendríamos el secreto disponible para usarlo en nuestro cluster. Además, también es posible descargar la clave privada si necesitamos descifrar un fichero. ¡ATENCIÓN! Esta tarea debe de llevarse a cabo sólo en situaciones de extrema necesidad. Un uso indebido de la clave privada podría comprometer todos los secretos del cluster.
kubectl get secret -l sealedsecrets.bitnami.com/sealed-secrets-key –
o yaml >sealed-secrets.key
Descifrar un secreto.
$ kubeseal –recovery-unseal –recovery-private-key sealed-
secrets.key -o yaml<sealed-secret.yaml
apiVersion: v1
data:
password: YmFyCg==
kind: Secret
metadata:
annotations:
sealedsecrets.bitnami.com/managed: «true»
sealedsecrets.bitnami.com/namespace-wide: «true»
creationTimestamp: null
name: foo
namespace: default
ownerReferences:
– apiVersion: bitnami.com/v1alpha1
controller: true
Kind : SealedSecret
name: foo
uid: » «
type: Opaque