Centralizando Secretos con Hashicorp Vault

Centralizando Secretos con Hashicorp Vault

¿Como hacer para que los secretos, que manejan nuestros DevOps, no queden en los servicios hardcodeados?. La solución es centralizarlos en Vault

¿Como hacer para que los secretos, que manejan nuestros DevOps, no queden en los servicios hardcodeados?.

La solución es centralizarlos en Vault. Este manejara secretos estáticos y dinámicos. Vamos a explorar el concepto de "Encryption As a Service".

Vamos a utilizar Docker, para esta prueba. Vamos a crear las carpetas, que requerimos para la gestion del docker-compose.yaml.

sudo mkdir Vault/vault && cd Vault/vault
sudo mkdir data policies logs config

Con la creación del Dockerfile & el docker-compose.yaml quedaria de esta manera.

santiago@ubuntu:~/Proyectos/Vault$ tree
.
├── docker-compose.yaml
└── vault
    ├── config
    │   └── vault-config.json
    ├── data
    ├── Dockerfile
    ├── logs
    └── policies

Creamos los archivos que nos estarían faltando. Por un lado el Dockerfile, que creara la imagen del container de Vault.

# Imagen Base
FROM alpine:3.11

# Agregamos la Ultima version. 
ENV VAULT_VERSION 1.6.3

# Creacion de Directorio
RUN mkdir /vault

# Descarga de Dependencias
RUN apk --no-cache add \
      bash \
      ca-certificates \
      wget

# Descarga de Vault y Configuracion
RUN wget --quiet --output-document=/tmp/vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip && \
    unzip /tmp/vault.zip -d /vault && \
    rm -f /tmp/vault.zip && \
    chmod +x /vault

# Path
ENV PATH="PATH=$PATH:$PWD/vault"

# Agregar Archivos de Configuracion
COPY ./config/vault-config.json /vault/config/vault-config.json

# Exponemos el Port 8200
EXPOSE 8200

# Run Vault
ENTRYPOINT ["vault"]

Dentro de config vamos a crear vault-config.json

{
    "backend": {
      "file": {
        "path": "vault/data"
      }
    },
    "listener": {
      "tcp":{
        "address": "0.0.0.0:8200",
        "tls_disable": 1
      }
    },
    "ui": true
  }

Como pueden ver para esta prueba hemos creado el Vault en Backend, esto no es una buena practica. Aca les dejo un enlace para configuración, como corresponde, un Backend. Agregamos el listener y la interfaz UI.

Por ultimo el docker-compose.yaml.

version: '3.7'

services:

  vault:
    build:
      context: ./vault
      dockerfile: Dockerfile
    container_name: vault
    ports:
      - 8200:8200
    volumes:
      - ./vault/config:/vault/config
      - ./vault/policies:/vault/policies
      - ./vault/data:/vault/data
      - ./vault/logs:/vault/logs
    environment:
      - VAULT_ADDR=http://127.0.0.1:8200
      - VAULT_API_ADDR=http://127.0.0.1:8200
    command: server -config=/vault/config/vault-config.json
    cap_add:
      - IPC_LOCK

Vamos a crear ese container con docker-compose up -d --build y revisamos si esta corriendo con docker ps:

santiago@ubuntu:~/Proyectos/Vault$ docker-compose up -d --build
Building vault
Step 1/9 : FROM alpine:3.11
 ---> 4666da2f166f
Step 2/9 : ENV VAULT_VERSION 1.4.0
 ---> Using cache
 ---> 28751b3bdd2a
Step 3/9 : RUN mkdir /vault
 ---> Using cache
 ---> 5add412a5e6d
Step 4/9 : RUN apk --no-cache add       bash       ca-certificates       wget
 ---> Using cache
 ---> 4ad54291195e
Step 5/9 : RUN wget --quiet --output-document=/tmp/vault.zip https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip &&     unzip /tmp/vault.zip -d /vault &&     rm -f /tmp/vault.zip &&     chmod +x /vault
 ---> Using cache
 ---> 0d57364580a7
Step 6/9 : ENV PATH="PATH=$PATH:$PWD/vault"
 ---> Using cache
 ---> 9c7f1ec59f98
Step 7/9 : COPY ./config/vault-config.json /vault/config/vault-config.json
 ---> Using cache
 ---> bfe379075af6
Step 8/9 : EXPOSE 8200
 ---> Using cache
 ---> 58279a7c4316
Step 9/9 : ENTRYPOINT ["vault"]
 ---> Using cache
 ---> 0ba547c00ab3

Successfully built 0ba547c00ab3
Successfully tagged vault_vault:latest
vault is up-to-date
santiago@ubuntu:~/Proyectos/Vault$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                    NAMES
276bf9952c73        vault_vault         "vault server -confi…"   About a minute ago   Up About a minute   0.0.0.0:8200->8200/tcp   vault

Ya tenemos corriendo nuestro Vault para comenzar a gestionar nuestros secretos, estáticos & dinámicos. Revisamos los logs, para poder iniciar nuestro Vault.

santiago@ubuntu:~/Proyectos/Vault$ docker-compose logs
Attaching to vault
vault    | ==> Vault server configuration:
vault    | 
vault    |              Api Address: http://127.0.0.1:8200
vault    |                      Cgo: disabled
vault    |          Cluster Address: https://127.0.0.1:8201
vault    |               Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
vault    |                Log Level: info
vault    |                    Mlock: supported: true, enabled: true
vault    |            Recovery Mode: false
vault    |                  Storage: file
vault    |                  Version: Vault v1.4.0
vault    | 
vault    | 2021-03-08T19:33:11.303Z [INFO]  proxy environment: http_proxy= https_proxy= no_proxy=
vault    | ==> Vault server started! Log data will stream in below:
vault    |

Vamos a conectarnos al Bash del contenedor para Inicializar el Vault con docker-compose exec vault bash y vamos a realizar estos comandos.

bash-5.0# vault operator init
Unseal Key 1: 9+Vd1eK/mU3wNUvyW08afht/HImvk7TQ24uo7ZRCLRbp
Unseal Key 2: rX/3vOqY9sfiWTcA2xcHg18yD/WtYwwjjUBBL2/E/3ft
Unseal Key 3: ZvcCCQtk9S0O+wikzpfZN36b4O9zLnDFWfuyrUE6epJ7
Unseal Key 4: qwboq8L9gLOkEJfRrmRxsxSgWN7cjx88lVc7cFt859kH
Unseal Key 5: SagtdjGSd4LJ+btbhFigmXJldDtJkVWetlJtFvZs/Deg

Initial Root Token: s.nFx20EKUhNbwBuhjjFIbQQf9

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated master key. Without at least 3 key to
reconstruct the master key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.

Tome nota de las claves de apertura y del token root. Deberá proporcionar tres de las claves de apertura cada vez que se vuelva a sellar o reiniciar el servidor de Vault. ¡Arrancamos! Voy a copiar las 3 primeras. Para entender ¿porque 3 claves? ver este enlace.

bash-5.0# vault operator unseal
Unseal Key (will be hidden):
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    1/3
Unseal Nonce       4ebecbd8-126b-bb54-68fe-bdc0151ae02b
Version            1.6.3
Storage Type       file
HA Enabled         false
bash-5.0# vault operator unseal
Unseal Key (will be hidden):
Key                Value
---                -----
Seal Type          shamir
Initialized        true
Sealed             true
Total Shares       5
Threshold          3
Unseal Progress    2/3
Unseal Nonce       4ebecbd8-126b-bb54-68fe-bdc0151ae02b
Version            1.6.3
Storage Type       file
HA Enabled         false
bash-5.0# vault operator unseal
Unseal Key (will be hidden):
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    5
Threshold       3
Version         1.6.3
Storage Type    file
Cluster Name    vault-cluster-3deaf32c
Cluster ID      ae759a82-deec-e0b5-97de-265c7bac6a6a
HA Enabled      false
bash-5.0#

¡Hecho! Ahora, usando el token root, podemos autenticarnos.

bash-5.0# vault login
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                s.nFx20EKUhNbwBuhjjFIbQQf9
token_accessor       KHACrJ4ej7gcw6c2JmQ30zD6
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

Vault ahora está abierto y listo para usar.

Vamos a encender la Auditoria, para ello corremos este comando.

bash-5.0# vault audit enable file file_path=/vault/logs/audit.log
Success! Enabled the file audit device at: file/

Crear los secretos para asegurar nuestra aplicación Python. Para ello vamos a conectarnos a Vault, como ya sabemos y parametrizar las variables necesarias. Supongamos que necesitamos manejar el password de una API, en Flask & Python, para que conecte a MySQL, ahí deberíamos usar el módulo de database para que Vault haga la gestión en la BBDD y la APP. Para esta PoC usaremos KV, ósea Key Value. Levantamos el plugin y agregamos un valor.

docker exec -it vault bash
bash-5.0# vault secrets enable kv
Success! Enabled the kv secrets engine at: kv/
bash-5.0# vault kv put kv/python password=tupassword
Success! Data written to: kv/python

Lo listamos o revisamos UI.

bash-5.0# vault kv get kv/python
====== Data ======
Key         Value
---         -----
password    tupassword

¡Excelente! Ya tenemos el valor en Vault. Ahora veamos la utilización de la libreria HVAC para la gestion del mismo en Python. A fines practicos estoy hardcodeando las variables, que deberian de ser variables de entorno.

import os

import hvac
import json 

client = hvac.Client()
client = hvac.Client(
 #url=os.environ['VAULT_URL'],
 #token=os.environ['VAULT_TOKEN']
 url='http://192.168.0.209:8200',
 token="s.tSz4Yjr58lZFrsQQTwDDYyMA"
)

json_formatted_str = json.dumps(client.read('kv/python'), indent=2)

print(json_formatted_str)

# Escribo Token con un Lease de 1 Hora
#client.write('kv/python', type='pythons', lease='1h')
# Imprimo Token
#print(client.read('kv/python'))

Simplemente voy a leer y a imprimir el JSON con el valor.

Hay muchas cosas por hacer, como utilizar Consul para el resguardo de las llaves, agregar SSL, revisar los plugins serán necesarios para las diferentes implementaciones. Espero les sirva y comiencen a investigar.