Instalando el baúl
Versión en este post es: Vault v1.18.3
Los pasos son desde la freebsd fundation son buenos y funcionan, solo se debe estar pendiente de que el servicio inicie correctamente
El vault.hcl
root@Hashicorp-Vault:/ # cat /usr/local/etc/vault.hcl
listener "tcp" {
address = "ip:8200" (1)
tls_disable = 1
}
storage "file" {
path = "/var/db/vault"
node_id = "v"
}
cluster_addr = "ip:8201" (2)
api_addr = "ip:8200" (3)
ui = true
1 | Es la direccion ip del jail |
2 | igual pero con puerto distinto. |
3 | igual. |
connection refused
Por alguna razón falla la conexion con el vault, en mi caso es por el fichero vault.hcl
no se esta leyendo correctamente, y nos puede lanzar este error.
Get "http://192.168.1.21:8200/v1/sys/seal-status": dial tcp 192.168.1.21:8200: connect: connection refused
root@vault-test:~ # vault server -config /usr/local/etc/vault.hcl
Error initializing core: Failed to lock memory: cannot allocate memory
This usually means that the mlock syscall is not available.
Vault uses mlock to prevent memory from being swapped to
disk. This requires root privileges as well as a machine
that supports mlock. Please enable mlock on your system or
disable Vault from using it. To disable Vault from using it,
set the `disable_mlock` configuration option in your configuration
file.
2025-01-27T11:02:28.636Z [INFO] proxy environment: http_proxy="" https_proxy="" no_proxy=""
2025-01-27T11:02:28.717Z [INFO] incrementing seal generation: generation=1
env o export
En nuestro caso ninguno funcionará, es por nuestra shell al iniciar en el jail.
env VAULT_ADDR=http://10.10.0.33:8200 vault operator init
Si no se setea el VAULT_ADD correctamente tenemos
WARNING! VAULT_ADDR and -address unset. Defaulting to https://127.0.0.1:8200.
Seteando environment correctamente
En el jail si hacemos un echo $SHELL
tenemos es /bin/csh
nos funcionara es usar el setenv
root@vault-test:/etc # setenv VAULT_ADDR ip:8200
root@vault-test:/etc # echo $VAULT_ADDR
ip:8200
root@vault-test:/etc # service vault start
Starting vault.
vault server -config
Este es el comando necesario para ejecucíon bajo fondo, muy importante para que la conexión se de.
vault server -config /usr/local/etc/vault/vault.hcl
root@vault-test:/etc # service vault start
Starting vault.
root@vault-test:/etc # vault operator init
Unseal Key 1: token
Unseal Key 2: token
Unseal Key 3: token
Unseal Key 4: token
Unseal Key 5: token
Initial Root Token: token
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 root key. Without at least 3 keys to
reconstruct the root 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.
root@vault-test:/etc # echo $SHELL
Guardando en el environment
Guardamos la variable en el fichero /etc/csh.cshrc
para que cada vez que entremos en el jail se encuentre la variable, y no tengamos error de connection refused
.
root@Hashicorp-Vault:~ # nano /etc/csh.cshrc
# System-wide .cshrc file for csh(1).
setenv VAULT_ADDR http://192.168.1.21:8200
Creando alias
Tan fácil como
alias vault-server-config="vault server -config /usr/local/etc/vault.hcl"
De momento, con cada inicio del jail debemos ejecutar esto vault-server-config .
|
Con Bastille usando zsh
Este template fácil se puede aplicar a un jail activo
PKG zsh
CMD chsh -s /usr/local/bin/zsh root
PKG ohmyzsh
CMD cp /usr/local/share/ohmyzsh/templates/zshrc.zsh-template ~/.zshrc
#zsh-autosuggestions
PKG powerline-fonts zsh-autosuggestions
CMD echo 'source /usr/local/share/zsh-autosuggestions/zsh-autosuggestions.zsh' >> ~/.zshrc (1)
1 | Para autocompletado. |
Deberia salir esto en la consola luego del proceso
➜ ~ echo $SHELL
/usr/local/bin/zsh
Luego se pueden aplicar las variables de entorno
export VAULT_ADDR=ip
export VAULT_TOKEN=token
Igual para el alias, editar el fichero .zshrc
La UI
Proceso de unseal
En la ui introducir 3 claves de esas 5 password que se generaron con el comando vault operator init
El seal desde la UI
Desde la UI, podemos sellar nuevamente el vault, jkaaja esto me recordo al baúl rojo de los recuerdos. 😂
Importante
|
root@Hashicorp-Vault:~ # vault read cubbyhole/mi.secreto
Error reading cubbyhole/mi.secreto: Error making API request.
URL: GET http://192.168.1.21:8200/v1/cubbyhole/mi.secreto
Code: 503. Errors:
* Vault is sealed (1)
1 | Baúl esta sellado/presintado |
Manejo de Secretos
Normalmente al buscar un secreto, tendremos error, porque necesitamos al menos Initial Root Token
en el environment.
Podemos setearlo con: |
setenv VAULT_TOKEN your-token (1)
nano /etc/csh.cshrc (2)
source /etc/csh.cshrc (3)
1 | seteando en el environmnet. |
2 | guardandolo permanente. |
3 | actualizamos el .cshrc |
Leyendo secreto por consola
Antes sin setear el token en el enviroment, no podriamos autenticarnos tal cual. |
root@Hashicorp-Vault:/ # vault read cubbyhole/mi.secreto
Error making API request.
URL: GET http://addreess/v1/sys/internal/ui/mounts/secret/my-secret
Code: 403. Errors:
* permission denied
Despues de setearlo |
root@Hashicorp-Vault:~ # vault read cubbyhole/mi.secreto
Key Value
--- -----
test mi.secreto
Guardando un secreto por consola
vault kv put secret/myapp my.property=mySecretValue
vault kv put path \ (1)
/myapp \ (2)
my.property (3)
=mySecretValue (4)
1 | path |
2 | nombre de la aplicacion |
3 | nombre de la password |
4 | valor |
Via http
El Initial Root Token
|
Nos servira para autenticarnos en la web vía token, también para consultar el api y obtener secretos, pro ejemplo con este curl podemos obtener un secreto
-
El header
X-Vault-Token
se envia al hacer la request, con elInitial Root Token
curl --location 'http://ip:8200/v1/cubbyhole/mi.secreto' \
--header 'Accept: */*' \
--header 'Accept-Language: es,en;q=0.9,fr;q=0.8,zh-TW;q=0.7,zh-CN;q=0.6,zh;q=0.5' \
--header 'Connection: keep-alive' \
--header 'DNT: 1' \
--header 'Referer: http://ip:8200/ui/vault/secrets/cubbyhole/list' \
--header 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' \
--header 'X-Vault-Token: YourToken' (1)
1 | Esto en realidad es el Initial Root Token |
Dando una respuesta asi
{
"request_id": "8ac9f6c5-9f08-12f3-ed87-70de1ab96842",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"test": "mi.secreto" (1)
},
"wrap_info": null,
"warnings": null,
"auth": null,
"mount_type": "cubbyhole" (2)
}
1 | El secreto |
2 | punto de montaje del secreto |
Plugin para PostgreSQL
Instalando el plugin para PostgreSQL
Nos permitira gestionar dinámicamente credenciales de bases de datos (usuarios y contraseñas). Vault puede generar credenciales temporales en PostgreSQL, lo que aumenta la seguridad y elimina la necesidad de gestionar manualmente las credenciales estáticas.
Podemos configurarlo medianta la UI o por consola
Desde la UI
Debenos crear un nuevo PostgreSQL database secrets engine y usamos databases
es bastante intuitivo
La cadena de conexión debe cumplir los requistios por ejemplo
postgresql://user:password@ip_address:port/database?
Habilitando TLS
Necesitamos hacer un par de cosas: |
-
Generar la CA.
-
Generar certificado de servidor.
-
Generar certificado de cliente.
Generando CA (certificate authority)
El certificate authority o autoridad certificadora lo usaremos para firmar otros certificados, crearemos una configuración llamada ca.conf
(es un fichero), el openSSL nos permitira crear tanto el ca-key.pem
y el ca-cert.pem
, por ejemplo:
-
el fichero
ca.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_ca
[ dn ]
CN = vault.server.local
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, keyCertSign, cRLSign
openssl genpkey -algorithm RSA -out ca-key.pem (1)
openssl req -x509 -new -nodes -key ca-key.pem -out ca-cert.pem -days 3650 -config ca.conf (2)
1 | Genera la clave privada de la CA |
2 | Genera el certificado de la CA (válido por 10 años 🔥) |
Generar certificado del servidor
Creamos el vault-cert-conf
para generar el vault-key.pem
y vault-cert.pem
-
el fichero
vault-cert-conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req
[ dn ]
CN = la-ip (1)
[ v3_req ]
subjectAltName = @alt_names
[ alt_names ]
IP.1 = la-ip
DNS.1 = vault.server.local
1 | Esta dirección ip puesta aquí, esta relacionada con el vault-cert.pem |
openssl req -x509 -newkey rsa:2048 -nodes -keyout vault-key.pem -out vault-cert.pem -days 365 -config vault-cert.conf
Esto debería salir en la consola, con el comando anterior:
Generating a RSA private key
.................+++++
...........................................................................................................................+++++
writing new private key to 'vault-key.pem'
-----
Quedando:
-
vault-key.pem
Clave privada del servidor -
vault-cert.pem
Certificado del servidor (válido para 'ip especificada, local en mi caso' / vault.server.local)
Tanto para el server y cliente, firmaremos los certificados con nuestro CA. |
openssl genpkey -algorithm RSA -out vault-key.pem (1)
openssl req -new -key vault-key.pem -out vault-req.csr -config vault-cert.conf (2)
(3)
openssl x509 -req -in vault-req.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out vault-cert.pem -days 365 -sha256 -extfile vault-cert.conf -extensions v3_req
1 | Genera la clave privada, aunque este paso es opcional, lo tenía generado al ejecutar el comando de aquí |
2 | Genera la CSR. |
3 | Firmando el CSR con el CA. |
Al firmar el CSR con el CA tendriamos esto:
Signature ok
subject=CN = ip (1)
Getting CA Private Key
1 | Dirección ip que usamos en el vault-cert.conf |
Generar Certificado cliente
Necesitamos generar una configuración client.conf
, porque somos flojos jajaja, también:
-
El
client-key.pem
-
La CSR (
client-req.csr
) -
Luego firmar la CSR con la CA para obtener
client-cert.pem
. -
el fichero
client.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[ dn ]
CN = vault-client
[ v3_req ]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
[ alt_names ]
DNS.1 = cliente.local
openssl genpkey -algorithm RSA -out client-key.pem (1)
(2)
openssl req -new -key client-key.pem -out client-req.csr -config client.conf
(3)
openssl x509 -req -in client-req.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365 -sha256 -extfile client.conf -extensions v3_req
1 | Genera la clave privada del cliente. |
2 | Crea la Solicitud de Firma (CSR) para el cliente. |
3 | Firmamos la CSR con nuestro CA para obtener el certificado del cliente. |
Finalizando esta parte deberiamos tener 2 ficheros, Clave privada del cliente y certificado de cliente firmado por nuestro CA.
-
client-key.pem
-
client-cert.pem
Generar Certificado cliente PKCS#12
Pasos para crear e importar el certificado de cliente en formato PKCS#12
openssl pkcs12 -export -out client-cert.p12 -inkey client-key.pem -in client-cert.pem -certfile ca-cert.pem -name "Vault Client Cert"
openssl pkcs12 -export \ (1)
-out client-cert.p12 \ (2)
-inkey client-key.pem \ (3)
-in client-cert.pem \ (4)
-certfile ca-cert.pem \ (5)
-name "Vault Client Cert" (6)
1 | Exporta el certificado en formato PKCS#12. |
2 | Nombre del archivo de salida, este lo importaremos el browser. |
3 | Clave privada del cliente. |
4 | Certificado del cliente. |
5 | Incluye la CA para que el navegador pueda validar la cadena de confianza. |
6 | Nombre visible en el navegador al seleccionarlo. |
Luego de ese comando nos pedira introducir la password para lo que sera la passphrase
Configuración vault.hcl
para TLS
Debemos configurar nuevamente el listener permitiendo la compatibilidad con TLS.
listener "tcp" {
address = "192.168.1.21:8200"
tls_cert_file = "/ruta/a/vault-cert.pem" (1)
tls_key_file = "/ruta/a/vault-key.pem" (2)
tls_client_ca_file = "/ruta/a/ca-cert.pem" (3)
}
storage "file" {
path = "/var/db/vault"
node_id = "v"
}
api_addr = "https://ip:8200"
ui = true
1 | Ruta al certificado del servidor |
2 | Ruta a la clave privada del servidor |
3 | Ruta al certificado de la CA (usado para verificar certificados de clientes) |
Importar el certificado al navegador
En mozilla, podemos ir al apartado de seguridad e importamos como en la siguiente imagen
Con Chrome por ejemplo chrome://settings/certificates
importamos el certificado llamado ca-cert.crt que es de tipo X.509 y luego reiniciamos chrome
|
Importar certificado del OS
En este caso andamos en linux
, y el vault en FreeBSD
, pero por la UI podemos acceder, aunque habilitar la UI en producción no se recomienda.
sudo cp vault-cert.crt /usr/local/share/ca-certificates/vault-cert.crt (1)
sudo update-ca-certificates (2)
Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
rehash: warning: skipping vault-cert.pem,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
1 | El certificado debe estar con extension .crt |
2 | Actualiza la base de datos de certificados del sistema. |
Ver certificados instalados en linux
awk -v cmd='openssl x509 -noout -subject' '/BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt
awk \ (1)
-v cmd='openssl x509 -noout -subject' \ (2)
'/BEGIN/{close(cmd)}; \ (3)
{print | cmd}' \ (4)
< /etc/ssl/certs/ca-certificates.crt (5)
1 | awk : tool para procesamiento, estructuración de texto. |
2 | -v cmd='openssl x509 -noout -subject'
|
3 | /BEGIN/{close(cmd)} :
|
4 | {print | cmd} :
|
5 | /etc/ssl/certs/ca-certificates.crt :
|
El resultado final es que el comando muestra la línea "subject" (que contiene información sobre el titular) de cada certificado CA instalado en el sistema, por ejemplo:
subject=C = US, O = Amazon, CN = Amazon Root CA 1
subject=C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
Endpoint /sys/health
Este endpoint está diseñado específicamente para permitir verificaciones básicas del estado del servidor sin autenticación, incluso sobre HTTPS.
curl -k https://192.168.1.21:8200/v1/sys/health
{
"initialized": true,
"sealed": true,
"standby": true,
"performance_standby": false,
"replication_performance_mode": "unknown",
"replication_dr_mode": "unknown",
"server_time_utc": 1739132639,
"version": "1.18.3",
"enterprise": false,
"echo_duration_ms": 0,
"clock_skew_ms": 0,
"replication_primary_canary_age_ms": 0
}
Haciendo GET via TLS
-
Fácilmente para obtener un curl de un secreto en especifico y rapido (como somos novatos), desde la UI podemos hacer esto.
copy/paste
Ahora cuando intentamos consultar un secreto via api rest tenemos esto
➜ ~ curl \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request GET \
https://192.168.1.21:8200/v1/${API_PATH} (1)
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
1 | Es donde tenemos el punto de montaje y el secreto |
Con nuestro certificado generado aquí podemos usarlo ahora para hacer las request.
➜ ~ curl -k --cert-type P12 --key client-cert.p12 \
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request GET \
https://vault.server.local:8200/v1/${PATH_SECRET}
==== salida
{
"request_id": "f2a77063-189c-2c29-2df8-8952962f0dc5",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"data": {
"secret.": "value" (1)
},
"metadata": {
"created_time": "2025-01-31T02:05:03.750285314Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 9
}
},
"wrap_info": null,
"warnings": null,
"auth": null,
"mount_type": "kv"
}
1 | El secreto en este array. |
Forzado de TLS sin nginx
De momento aun con todos los pasos realizados, si entramos a la url principal del vault tengamos o no certificado, podemos entrar y visualizar la UI, y deberia de rechazarnos, entonces debemos añadir en nuestro listener tcp
una linea.
listener "tcp" {
address = "ip-address:8200"
tls_cert_file = "/root/vault-cert.pem"
tls_key_file = "/root/vault-key.pem"
tls_client_ca_file = "/root/ca-cert.pem"
tls_disable_client_certs = false # Forzar autenticación de cliente
tls_min_version = "tls12"
tls_require_and_verify_client_cert = "true" (1)
}
1 | Linea mágica, forzara digamos el mTLS, autenticación mutua |
➜ ~ curl --cert client-cert.pem --key client-key.pem --cacert ca-cert.pem \ (1)
--header "X-Vault-Token: ${VAULT_TOKEN}" \
--request GET \
https://vault.server.local:8200/v1/${PATH_SECRET}
==== salida
{
"request_id": "f2a77063-189c-2c29-2df8-8952962f0dc5",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"data": {
"secret.": "value" (2)
},
"metadata": {
"created_time": "2025-01-31T02:05:03.750285314Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 9
}
},
"wrap_info": null,
"warnings": null,
"auth": null,
"mount_type": "kv"
}
1 | Los archivo en formato .pem son más compatibles con curl por lo visto, y es la única manera de que funcione el curl en esta prueba. |
2 | El secret |
Transit Secrets Engine de Vault
"encryption as a service"
Aka cifrado como servicio, aplica criptografía simétrica a nuestras passwords, útil para casos de necesitar la password en texto plano 🤐…
Ese método de cifrado reversible, puede venir útil, por ejemplo para una request con un usuario y password a un sistema remoto mi caso…
Como tenemos ahora TLS tocaria ajustar un poco nuestro environment con
export VAULT_ADDR=https://direccion-ip:8200
export VAULT_CLIENT_CERT=/path/client-cert.pem
export VAULT_CLIENT_KEY=/path/client-key.pem
export VAULT_CACERT=/path/ca-cert.pem
export VAULT_TOKEN=token (1)
1 | Token, pero ojo, ya no deberia ser el "initial root token" |
Genera token
El initial root token generado, cuando inicia el operador es un token root, ya sabemos como es esto, entonces lo mejor sera crear una politica y a esa politica generada le asignamos un token.
Cualquier persona con este token puede borrar/leer/escribir cualquier dato en Vault.
How do we do it ? from the UI or by console?
shell
We create a file called kv-lector.hcl
path "app/data/test" {
capabilities = ["read", "list"]
}
path "app/metadata/test" {
capabilities = ["list"]
}