Gestores de secretos autoalojados comparados
Compara OpenBao, Infisical, SOPS con age, credenciales de systemd y un archivo env protegido para elegir secretos en un solo VPS y conocer su coste.
Qué hace un gestor de secretos autoalojado que no hace un gestor de contraseñas
Un gestor de secretos autoalojado entrega credenciales a los procesos. Un gestor de contraseñas entrega credenciales a las personas. Todo lo demás se deriva de esa diferencia. Una persona presente y atenta desbloquea un gestor de contraseñas. Un gestor de secretos debe entregar la contraseña de una base de datos a una aplicación a las 03:00, cuando nadie está despierto.
Los modos de fallo son distintos, y eso es importante. Un gestor de contraseñas bloqueado es una molestia: se vuelve a escribir la contraseña maestra. Un gestor de secretos sellado provoca una interrupción del servicio: cada servicio que se reinicia mientras está sellado arranca sin credenciales y permanece detenido. Ejecutar Vaultwarden como gestor de contraseñas propio resuelve bien el problema de las personas. No resuelve el problema de las máquinas, y nunca se diseñó para hacerlo.
Las opciones realistas para un servidor se dividen en dos grupos. OpenBao e Infisical son servicios: una API, una base de datos, TLS (seguridad de la capa de transporte), un paso de inicio de sesión y un proceso que ahora debe mantenerse activo. SOPS con age, las credenciales de systemd y los secretos de Docker son archivos: se cifran en reposo, algo que ya está en ejecución los descifra y no hay nada adicional que supervisar.
Esta es la respuesta directa. Para un único servidor con una o dos personas, las opciones basadas en archivos suelen ser las adecuadas. Un OpenBao que nadie desella correctamente y cuyas credenciales nadie rota es peor que un archivo de entorno con modo 600, porque añade un componente que puede fallar y una copia de seguridad que se configurará mal, sin aportar ninguna rotación que no se estuviera haciendo manualmente.
¿Es suficiente un archivo de entorno con permisos 600?
A menudo, sí. La amenaza contra la que protege es que otro usuario del sistema lea la contraseña de la base de datos. Los permisos de archivos de Unix lo impiden, y lo hacen antes de que la red esté activa.
sudo install -d -m 750 -o root -g myapp /etc/myapp
sudo install -m 640 -o root -g myapp /dev/null /etc/myapp/env
sudoedit /etc/myapp/envCompruébelo desde ambos lados:
sudo -u myapp cat /etc/myapp/env
sudo -u nobody cat /etc/myapp/envEl primero muestra el archivo. El segundo muestra cat: /etc/myapp/env: Permission denied, porque nobody no pertenece al grupo myapp y el archivo no tiene permisos para otros usuarios. Ese es todo el modelo de seguridad, y es un modelo real.
La filtración ocurre después. Una unidad de systemd con EnvironmentFile= copia esos valores al entorno del proceso, y el entorno del proceso se puede leer.
[Service]
User=myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/usr/local/bin/myappsudo cat /proc/$(pgrep -n myapp)/environ | tr '\0' '\n'Eso muestra sus secretos en texto claro, porque /proc/<pid>/environ se puede leer como root y como el usuario con el que se ejecuta el proceso. Un informador de errores que adjunte el entorno a un informe verá lo mismo. También cualquier herramienta que se ejecute con la misma cuenta. Por eso mantener los secretos fuera de los agentes de IA empieza por sacarlos del entorno. Combine el archivo con un usuario de servicio dedicado con pocos privilegios para que «el usuario con el que se ejecuta el proceso» no sea root.
SOPS con age: secretos cifrados que puede confirmar en git
SOPS (secrets operations) cifra los valores de un archivo YAML o JSON y deja las claves en texto claro. age es una herramienta de cifrado pequeña que proporciona un único par de claves y ningún servidor de claves. Juntos permiten confirmar secrets.enc.yaml junto al código, y git diff sigue mostrando qué configuración cambió sin revelar a los lectores en qué se convirtió.
age está incluido en Ubuntu 24.04. SOPS no lo está, así que descargue .deb de la página de versiones. La versión 3.13.3 era la actual en agosto de 2026.
sudo apt update && sudo apt install -y age
curl -LO https://github.com/getsops/sops/releases/download/v3.13.3/sops_3.13.3_amd64.deb
sudo apt install -y ./sops_3.13.3_amd64.deb
sops --versionGenere un par de claves. age-keygen escribe la clave privada en el archivo e imprime la clave pública, por lo que verá una línea que comienza por Public key: age1....
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
age-keygen -y ~/.config/sops/age/keys.txtColoque la clave pública en .sops.yaml en la raíz del repositorio, para no tener que recordar el destinatario en la línea de comandos.
creation_rules:
- age: age1s3cqcks5genc6ru8chl0hkkd04zmxvczsvdxq99ekffe4gmvjpzsedk23csops encrypt secrets.yaml > secrets.enc.yaml
sops decrypt secrets.enc.yamlUna regla sin path_regex coincide con todo, que es lo que necesita inicialmente. Si añade una más adelante, escríbala para que coincida con el archivo que pasa a sops, porque las reglas se comprueban contra la ruta de entrada y no contra el archivo al que redirige la salida.
En tiempo de ejecución, entregue los valores a un solo proceso y a ningún otro:
sops exec-env secrets.enc.yaml './myapp'sops exec-env descifra los valores en memoria y los establece en el entorno del proceso hijo, por lo que no se escribe texto claro en el disco. La salvedad sobre el entorno de la sección anterior sigue siendo aplicable a ese proceso hijo.
Aquí hay dos problemas habituales. El error Failed to get the data key required to decrypt the SOPS file con systemd casi siempre significa que SOPS buscó en el directorio de inicio equivocado, porque una unidad no hereda su HOME. Establezca la ruta explícitamente con Environment=SOPS_AGE_KEY_FILE=/etc/sops/age.txt en la unidad. Por separado, editar .sops.yaml no vuelve a cifrar nada de lo que ya existe: añadir la clave pública de un colega sólo afecta a los archivos nuevos, así que ejecute sops updatekeys secrets.enc.yaml en cada archivo existente. Si su configuración ya se ejecuta mediante Ansible, cifrar los mismos valores con Ansible Vault permite obtener el mismo resultado sin una segunda herramienta.
Credenciales de systemd: secretos que nunca llegan al entorno
Ubuntu 24.04 incluye systemd 255, por lo que no es necesario instalar nada. systemd-creds cifra un secreto en el host y systemd lo descifra en un directorio privado que sólo puede leer ese servicio.
sudo systemd-creds setup
sudo install -d -m 700 /etc/myapp
echo -n 'hunter2' | sudo systemd-creds encrypt --name=db_password - /etc/myapp/db_password.cred[Service]
User=myapp
LoadCredentialEncrypted=db_password:/etc/myapp/db_password.cred
ExecStart=/usr/local/bin/myappEl servicio lee el valor desde un archivo llamado db_password dentro del directorio indicado por $CREDENTIALS_DIRECTORY. El valor no está en el entorno, por lo que /proc/<pid>/environ no muestra nada útil y el texto en claro nunca llega al sistema de archivos raíz.
Verifique que el archivo se descifra antes de asignarlo a una unidad:
sudo systemd-creds decrypt /etc/myapp/db_password.cred -Debe saber qué clave lo cifró, porque de ello depende que la copia de seguridad sea útil. El valor predeterminado --with-key=auto usa el chip TPM2 (módulo de plataforma segura versión 2) cuando está presente y disponible, y usa la clave del host en caso contrario. La mayoría de las instancias VPS no tienen TPM2.
systemd-analyze has-tpm2no significa que se usó la clave del host. Esa clave se encuentra en /var/lib/systemd/credential.secret y sólo root puede leerla. Si restaura db_password.cred en un VPS nuevo sin ese archivo, nada podrá descifrarlo. Copie credential.secret en la misma copia de seguridad o conserve el texto en claro en un lugar al que todavía pueda acceder.
Secretos de Docker: archivos en /run/secrets
Compose lee un archivo del host y lo monta en el contenedor en /run/secrets/<name>.
services:
app:
image: myapp:latest
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txtdocker compose exec app cat /run/secrets/db_password
docker compose exec app env | grep -i passwordEl primero muestra el secreto. El segundo muestra sólo DB_PASSWORD_FILE=/run/secrets/db_password, que es precisamente el objetivo: el valor nunca está en el entorno del contenedor, por lo que no aparece en la salida de docker inspect. Muchas imágenes oficiales ya esperan este formato, y la imagen de Postgres lee POSTGRES_PASSWORD_FILE exactamente de esta forma.
Debe quedar claro qué ofrece este mecanismo. Fuera del modo Swarm no hay cifrado en ninguna capa: ./db_password.txt es un archivo de texto plano en el host, y su única protección son sus permisos y su propietario. Establezca ambos manualmente, porque Compose montará sin avisar un archivo legible para todos. El conjunto más amplio de ventajas y limitaciones frente al acceso directo mediante env_file se explica en la guía sobre archivos de entorno y secretos de Compose.
Qué coste real tienen OpenBao y Vault
OpenBao es el fork de HashiCorp Vault de la Linux Foundation. Se inició después de que HashiCorp cambiara la licencia de Vault a Business Source License en 2023. OpenBao mantiene la licencia MPL 2.0 (Mozilla Public License). La versión 2.6.2 era la actual en agosto de 2026. Casi todo lo que se explica a continuación también se aplica a Vault, porque el fork mantuvo la misma interfaz de comandos.
docker pull docker.io/openbao/openbaoLos paquetes para Debian y Ubuntu están disponibles en la página de descargas de OpenBao si prefiere que apt gestione las actualizaciones. El servidor necesita un archivo de configuración que defina un listener y un backend de almacenamiento:
listener "tcp" {
address = "127.0.0.1:8200"
tls_cert_file = "/path/to/full-chain.pem"
tls_key_file = "/path/to/private-key.pem"
}
storage "raft" {
path = "/path/to/raft/data"
node_id = "raft_node_1"
}Después, inícielo una vez:
bao operator initDe forma predeterminada, divide la clave root en 5 fragmentos y exige 3 para quitar el sellado. Esas opciones son los flags -key-shares y -key-threshold. Muestra los fragmentos y el token root inicial una sola vez y no vuelve a mostrarlos.
Esta es la parte que omiten la mayoría de las comparativas. Un servidor reiniciado está sellado. OpenBao mantiene la clave root sólo en memoria. Por eso, después de un reinicio, no puede descifrar su propio almacenamiento hasta que alguien proporcione el umbral de fragmentos. Una actualización del kernel o una terminación por falta de memoria dejan el servidor sellado y las aplicaciones no pueden iniciar sesión.
En un VPS administrado por una sola persona, la división de Shamir no protege nada, porque los 5 fragmentos acaban en el mismo gestor de contraseñas de la misma persona. El desellado automático mueve la clave a un dispositivo o servicio de confianza. En una nube grande, suele ser un servicio de gestión de claves. En un VPS, normalmente es un archivo de claves situado en el mismo disco que los datos que protege. Esto reduce realmente la seguridad, a cambio de que el servidor vuelva a funcionar por sí solo después de un reinicio. Tome esta decisión de forma consciente y documente qué opción ha elegido.
Infisical: una interfaz web, una base de datos y una clave maestra que debe conservar
Infisical es una plataforma de secretos con interfaz web, proyectos, entornos y control de acceso por usuario. El alojamiento autónomo con Compose es breve:
curl -o docker-compose.prod.yml https://raw.githubusercontent.com/Infisical/infisical/main/docker-compose.prod.yml
curl -o .env https://raw.githubusercontent.com/Infisical/infisical/main/.env.example
docker compose -f docker-compose.prod.yml up -dEdite .env antes de ejecutar el último comando. Dos valores deben ser propios y uno de ellos no debe cambiar nunca después:
openssl rand -hex 16
openssl rand -base64 32El primero es ENCRYPTION_KEY, una cadena hexadecimal de 16 bytes. Es la clave con la que se cifran los secretos dentro de PostgreSQL. Si la pierde, una copia de seguridad correcta de la base de datos se convierte en un conjunto de datos cifrados sin utilidad. Si la cambia en una instancia en ejecución, los secretos existentes dejan de poder descifrarse. El segundo es AUTH_SECRET, una cadena base64 de 32 bytes usada para las sesiones. SITE_URL debe ser la URL absoluta a la que accederá realmente, incluido el protocolo, o la redirección del inicio de sesión fallará.
Infisical encaja mejor que OpenBao cuando lo que realmente necesita son usuarios: una interfaz web para un equipo pequeño y separación entre entornos, en lugar de credenciales de base de datos que caducan por sí solas. Requiere PostgreSQL, Redis y un certificado TLS, y ahora debe aplicarles parches y crear copias de seguridad.
Qué ocurre cuando el servicio de secretos está caído y la aplicación se reinicia
Esta pregunta determina si un servicio de secretos debe ejecutarse en un único servidor. Los archivos se pueden leer antes de que se inicie la red. Un servicio no.
Reinicie el servidor y la aplicación y OpenBao se iniciarán al mismo tiempo. La aplicación solicita la contraseña de la base de datos, OpenBao todavía está sellado, la solicitud falla y systemd reinicia la aplicación en un bucle hasta que un administrador introduce manualmente las partes necesarias para quitar el sellado. No hay nada averiado. Tampoco hay nada operativo.
Hay dos formas claras de resolverlo. Ordene las unidades y permita que la aplicación reintente: After= el servicio de secretos, además de Restart=on-failure y un RestartSec= lo bastante largo para no saturar la API. O recupere el secreto durante el despliegue en lugar de hacerlo durante el arranque: genere el secreto en un archivo con permisos 600 o en una credencial de systemd, de modo que el sistema en ejecución dependa de un archivo y no de una API.
La caducidad del token plantea el mismo problema, pero en un intervalo más largo. Los tokens y las concesiones de OpenBao tienen un tiempo de vida, por lo que un proceso de larga duración que nunca los renueva pierde el acceso en un momento no relacionado con ningún despliegue. Este fallo resulta confuso precisamente porque ese día no cambió nada.
Copia de seguridad del almacén
Todas las opciones de esta sección tienen una clave, y una copia de seguridad sin esa clave no sirve. Anote dónde se encuentra la suya.
En un archivo de entorno, el archivo es el secreto, por lo que la copia de seguridad debe estar cifrada. Con SOPS, el archivo cifrado puede almacenarse en cualquier ubicación pública, y la clave privada age en ~/.config/sops/age/keys.txt es lo que no debe perder. Para las credenciales de systemd, haga una copia de seguridad de /var/lib/systemd/credential.secret junto con los archivos .cred. Para Infisical, cree un volcado de PostgreSQL y almacene ENCRYPTION_KEY en una ubicación separada.
OpenBao con almacenamiento raft crea su propia instantánea:
bao operator raft snapshot save backup.snap
bao operator raft snapshot restore backup.snapLa instantánea contiene el almacenamiento cifrado, por lo que restaurarla en un servidor nuevo todavía requiere las partes de desbloqueo de bao operator init. Un trabajo nocturno que copie las instantáneas en un almacenamiento de objetos mientras las partes se guardan en ninguna ubicación no es una copia de seguridad de nada. Pruebe la restauración en un VPS desechable antes de depender de ella.
Registro de auditoría: quién leyó qué secreto
Los archivos no proporcionan un registro de auditoría. El modo y el propietario indican quién podía leer el secreto. Nunca indican quién lo hizo. auditd con una supervisión de la ruta es el sustituto más cercano, y registra que se abrió un archivo, no qué valor se utilizó.
OpenBao registra cada solicitud en un dispositivo de auditoría que se habilita explícitamente:
bao audit enable file file_path=/var/log/openbao_audit.logHay dos aspectos de ese registro que cambian la forma de administrar el servidor. La mayoría de las cadenas de las solicitudes y respuestas se procesan con HMAC-SHA256 y una sal. Así, puede comparar con el registro un valor que ya conoce sin que el propio registro contenga el texto sin cifrar. Los enteros y los valores booleanos se escriben en claro, por lo que un secreto numérico no obtiene ninguna protección mediante ese hash.
Después aparece el problema operativo: OpenBao no responde a las solicitudes cuando ningún dispositivo de auditoría habilitado puede registrarlas. Además, un dispositivo que falla de forma bloqueante hace que las solicitudes se queden colgadas hasta que alguien lo soluciona. Un disco lleno en /var/log deja fuera de servicio la API de secretos por diseño. Asigne espacio propio al registro de auditoría y configure una regla de logrotate desde el primer día, no después de la primera interrupción.
¿Qué gestor de secretos autoalojado debería ejecutar?
Cuente las máquinas y las personas, y después elija.
- Una máquina y una persona: use un archivo de entorno con permisos
600, propiedad deroot, que lea un usuario de servicio. Añada credenciales desystemdcuando quiera que el valor no esté en el entorno del proceso. - Una máquina, de dos a cinco personas y la configuración ya almacenada en git: use SOPS con age. Cada persona obtiene un par de claves y
.sops.yamlenumera todas las claves públicas autorizadas para descifrar. - Varias máquinas, un repositorio de configuración y sin necesidad de credenciales que caduquen: use SOPS con age igualmente, con una clave destinataria por host, de modo que una clave de host robada sólo descifre los archivos de ese host.
- Varias máquinas y varios equipos que realmente necesitan credenciales de base de datos con una duración limitada, además de un registro de auditoría que alguien revise: use OpenBao y reserve una hora al mes de trabajo del operador para las prácticas de apertura y restauración.
La regla común a los cuatro casos es la misma. Ejecute la solución más pequeña que cumpla un requisito que pueda expresar claramente, porque un gestor de secretos que está caído no se puede distinguir de un gestor de secretos vacío.
FAQ
¿Merece la pena usar un gestor de secretos autohospedado en un único VPS?
Normalmente no, si se refiere a un servicio como OpenBao o Infisical. En un solo equipo con una o dos personas, un archivo de entorno con permisos mode 600 o una credencial cifrada de systemd ofrece la misma protección frente a otro usuario local, sin un paso de desbloqueo y sin otro servicio que actualizar. Un servicio de secretos empieza a compensar cuando tiene varias máquinas y varias personas, o cuando necesita credenciales que caduquen sin que nadie tenga que rotarlas manualmente.
¿Cuál es la diferencia entre un gestor de contraseñas y un gestor de secretos?
Un gestor de contraseñas almacena credenciales que introduce una persona, y una persona lo desbloquea mientras está presente. Un gestor de secretos entrega credenciales a procesos, por lo que debe funcionar a las 03:00 sin que nadie lo supervise. Esa es la diferencia principal: un gestor de contraseñas bloqueado le obliga a volver a escribir una contraseña maestra, mientras que un gestor de secretos sellado detiene todos los servicios que se reinician mientras permanece sellado.
¿Qué ocurre con mis aplicaciones si OpenBao queda sellado después de un reinicio?
No pueden obtener sus secretos, por lo que no arrancan, y systemd las reinicia en bucle hasta que alguien proporciona el umbral de desbloqueo, que de forma predeterminada es 3 de 5 fragmentos. OpenBao mantiene la clave raíz sólo en memoria, por lo que cada reinicio vuelve a sellarlo. Puede activar el desbloqueo automático, aceptando que en un único VPS la clave de desbloqueo termina en el mismo disco que los datos, o generar los secretos en un archivo durante el despliegue para que el arranque nunca dependa de la API.
¿Puedo confirmar archivos cifrados con SOPS en un repositorio público?
Los valores están cifrados, por lo que están protegidos frente a cualquiera que no tenga la clave privada de age. Las claves no están cifradas: un lector puede ver que conserva STRIPE_SECRET_KEY y SMTP_PASSWORD, y con qué frecuencia cambia cada una. Esos metadatos son aceptables para la mayoría de los proyectos y no lo son para algunos. Mantenga la clave privada de age fuera del repositorio y ejecute sops updatekeys en todos los archivos existentes cada vez que añada o elimine un destinatario.