Despliegue de OpenBao en un VPS: gestión de secretos, TLS, AppRole y copias de seguridad
TL;DR
En esta guía desplegaremos OpenBao en Ubuntu 24.04 LTS, activaremos el almacenamiento Raft, protegeremos la interfaz web y la API mediante HTTPS, crearemos una política y un rol AppRole para la aplicación y, a continuación, configuraremos copias de seguridad automáticas cifradas de snapshots de Raft.
- OpenBao funcionará como un servicio systemd con un usuario de sistema independiente.
- Los datos se almacenarán en el Raft Storage integrado sin una base de datos independiente.
- El acceso público a la API se organizará mediante Caddy y un certificado TLS de Let’s Encrypt.
- Las aplicaciones obtendrán secretos mediante AppRole con los permisos mínimos necesarios.
- Los snapshots del almacenamiento se cargarán periódicamente en un bucket externo compatible con S3 mediante restic.
- Todas las operaciones se verifican con los comandos OpenBao CLI, curl y systemctl.
1. TL;DR
En esta guía desplegaremos OpenBao en Ubuntu 24.04 LTS, activaremos el almacenamiento Raft, protegeremos la interfaz web y la API mediante HTTPS, crearemos una política y un rol AppRole para la aplicación y, a continuación, configuraremos copias de seguridad automáticas cifradas de snapshots de Raft.
- OpenBao funcionará como un servicio systemd con un usuario de sistema independiente.
- Los datos se almacenarán en el Raft Storage integrado sin una base de datos independiente.
- El acceso público a la API se organizará mediante Caddy y un certificado TLS de Let’s Encrypt.
- Las aplicaciones obtendrán secretos mediante AppRole con los permisos mínimos necesarios.
- Los snapshots del almacenamiento se cargarán periódicamente en un bucket externo compatible con S3 mediante restic.
- Todas las operaciones se verifican con los comandos OpenBao CLI, curl y systemctl.
2. Contenido
El artículo está dirigido a un administrador que despliega su propio almacenamiento de secretos para un pequeño SaaS, CI/CD, servicios internos o infraestructura doméstica. Los comandos se proporcionan para Ubuntu 24.04 LTS y la arquitectura amd64.
El ejemplo utiliza un único nodo OpenBao. No es un clúster de alta disponibilidad: si el VPS falla, el servicio no estará disponible hasta que la máquina se restaure, pero los datos pueden recuperarse desde un snapshot de Raft. Al final del artículo se analizan opciones de escalado para infraestructura crítica de producción.
3. Qué configuramos y por qué
El problema de los secretos en archivos de configuración
Las contraseñas de bases de datos, los tokens de API, las claves de proveedores cloud y los certificados suelen terminar en Git, Docker Compose, variables de CI o copias de seguridad de servidores. Incluso si se elimina un secreto del último commit, permanecerá en el historial del repositorio. Otro problema es la falta de auditoría: normalmente no está claro qué aplicación leyó qué secreto y cuándo.
OpenBao resuelve esta tarea como un almacenamiento centralizado de secretos con control de acceso. Los secretos se guardan dentro del storage backend, y el cliente primero se autentica, tras lo cual obtiene acceso únicamente a las rutas permitidas. OpenBao es compatible con el modelo habitual de API y CLI de Vault, pero se desarrolla como un proyecto independiente con licencia abierta.
Arquitectura de nuestro ejemplo
| Componente | Propósito | Dirección |
|---|---|---|
| OpenBao | Almacenamiento de secretos y API | 127.0.0.1:8200 |
| Raft Storage | Almacenamiento de datos y registro de consenso | /opt/openbao/data |
| Caddy | HTTPS, certificado, reverse proxy | https://bao.example.com |
| AppRole | Autenticación de máquinas y aplicaciones | role_id y secret_id |
| restic | Cifrado y envío de copias de seguridad | Almacenamiento compatible con S3 |
Resultado final
Después de seguir las instrucciones, se creará un nodo OpenBao con el secrets engine KV v2. En él se podrán guardar, por ejemplo, database/password o production/api-key. La aplicación iniciará sesión mediante AppRole, obtendrá un token de corta duración y leerá solo la ruta necesaria.
Desde el exterior, solo estará disponible el puerto TCP 443. El puerto 8200 de OpenBao permanecerá cerrado a la red externa, ya que la API estará disponible localmente a través de Caddy. SSH se permitirá únicamente desde la IP administrativa o mediante VPN.
Self-hosted o cloud-managed
Los servicios gestionados de secretos son cómodos: el proveedor se encarga de las actualizaciones, la tolerancia a fallos y parte de las copias de seguridad. Sus desventajas son el coste, la dependencia de una plataforma externa, las restricciones regionales y la necesidad de confiar información crítica al proveedor.
OpenBao self-hosted en un VPS es adecuado si se necesita controlar la ubicación de los datos, utilizar procedimientos propios de recuperación o reducir los gastos recurrentes. A cambio, el administrador es responsable de los parches, TLS, monitorización, copias de seguridad y el procedimiento de recuperación. Un único VPS no puede considerarse un sustituto de un clúster completamente tolerante a fallos.
Lo que es importante saber sobre seal y unseal
Durante la inicialización, OpenBao crea un root token y claves unseal. En producción, no se deben almacenar en un archivo en el servidor. Para el ejemplo de laboratorio se utiliza el esquema clásico Shamir con cinco claves y un umbral de tres: se necesitan tres claves cualesquiera para desbloquearlo.
La pérdida del root token no destruye los datos, pero complica la administración. La pérdida de un número suficiente de claves unseal hace imposible descifrar el almacenamiento. Las claves deben distribuirse entre administradores de confianza y guardarse offline, por ejemplo, en un gestor de contraseñas y en una copia de seguridad física cifrada.
4. Qué configuración de VPS se necesita para esta tarea
OpenBao no requiere mucha CPU si el número de solicitudes es reducido. Los requisitos principales son un disco estable, suficiente memoria RAM y snapshots regulares. Para un solo nodo, la fiabilidad del almacenamiento y la disponibilidad de una copia de seguridad remota son más importantes que decenas de núcleos virtuales.
| Escenario | CPU | RAM | Disco | Red |
|---|---|---|---|---|
| Laboratorio | 1 vCPU | 1 GB | 20 GB SSD | 100 Mbit/s |
| Instalación de producción pequeña | 2 vCPU | 2–4 GB | 40–80 GB SSD/NVMe | 100–1000 Mbit/s |
| Varios equipos y CI/CD | 4 vCPU | 8 GB | 100 GB NVMe | 1 Gbit/s |
Una opción básica práctica es 2 vCPU, 4 GB de RAM, 60 GB SSD, IPv4 público o IPv6 accesible, red privada si hay varios nodos y snapshots diarios del disco. Para esta configuración puede elegir un VPS adecuado con SSD, IP estática y capacidad para conectar una copia de seguridad S3 externa.
20 GB de disco son suficientes solo para una pequeña cantidad de secretos y registros. OpenBao almacena los secretos de forma compacta, pero el directorio de trabajo de Raft, los registros del sistema y los archivos temporales siguen requiriendo espacio adicional. No se debe utilizar un disco lleno: si falta espacio, la escritura en Raft puede detenerse.
Cuándo se necesita un dedicated
Un servidor dedicado se justifica si OpenBao atiende miles de aplicaciones, almacena un gran volumen de secretos dinámicos, funciona junto con varios servicios pesados o requiere un rendimiento de disco predecible. Dedicated también es útil ante requisitos estrictos de aislamiento físico y control local del hardware.
Para una única instancia pequeña de OpenBao, dedicated suele ser excesivo. Es más racional destinar el presupuesto a tres VPS en zonas diferentes, almacenamiento externo de copias de seguridad, monitorización y pruebas de recuperación. Es importante no confundir un servidor dedicado con un clúster: un único dedicated sigue siendo un único punto de fallo.
Impacto de la ubicación
La ubicación afecta a la latencia entre las aplicaciones y la API de OpenBao, los requisitos de localización de datos y el tiempo de recuperación. Instale OpenBao cerca de las aplicaciones que lo consultan durante el arranque u obtienen regularmente tokens de corta duración. Es recomendable mantener el bucket S3 de respaldo en otra región tolerante a fallos.
Si se utilizan tres nodos Raft, la latencia entre ellos debe ser baja y estable. Para el clúster, es mejor elegir una plataforma regional con varias zonas de disponibilidad en lugar de extender el consenso entre continentes.
5. Preparación del servidor
A continuación se asume un Ubuntu Server 24.04 LTS limpio con acceso por SSH mediante un usuario creado por el proveedor. Sustituya 203.0.113.10, admin y bao.example.com por sus propios valores. Todos los comandos se ejecutan desde un usuario administrativo con permisos sudo.
Creación de administrador y clave SSH
Si ya tiene un usuario sudo independiente, puede omitir este paso. No cierre la sesión SSH actual hasta que compruebe el nuevo inicio de sesión en una ventana independiente.
# Создаём отдельного пользователя для администрирования
sudo adduser ops
# Добавляем пользователя в группу sudo
sudo usermod -aG sudo ops
# Создаём каталог для публичного ключа
sudo install -d -m 700 -o ops -g ops /home/ops/.ssh
# Копируем ключ текущего пользователя в новый аккаунт
sudo cp ~/.ssh/authorized_keys /home/ops/.ssh/authorized_keys
# Исправляем владельца и права файла ключей
sudo chown ops:ops /home/ops/.ssh/authorized_keys
sudo chmod 600 /home/ops/.ssh/authorized_keys
Compruebe el inicio de sesión en una nueva terminal:
# Подключаемся с локального компьютера и проверяем sudo
ssh [email protected]
sudo -v
whoami
Actualización del sistema y utilidades básicas
# Обновляем индексы пакетов и устанавливаем исправления
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt full-upgrade -y
# Устанавливаем инструменты для установки и диагностики
sudo apt install -y curl unzip jq ca-certificates gnupg lsb-release \
ufw fail2ban chrony unattended-upgrades restic awscli
# Перезагружаем сервер, если обновилось ядро
sudo reboot
Tras reiniciar, vuelva a conectarse por SSH. Active la instalación automática de actualizaciones de seguridad. El reinicio de OpenBao tras una actualización debe realizarse durante una ventana de mantenimiento previamente seleccionada.
# Включаем автоматические обновления безопасности Ubuntu
sudo dpkg-reconfigure -plow unattended-upgrades
# Проверяем синхронизацию времени
timedatectl status
chronyc tracking
Configuración de SSH
Desactive el inicio de sesión con contraseña solo después de comprobar la autenticación con clave. Para un servidor público también se prohíbe el inicio de sesión como root. El puerto SSH puede cambiarse, pero esto no sustituye la autenticación mediante clave ni el firewall.
# Создаём отдельный drop-in для sshd
sudo tee /etc/ssh/sshd_config.d/ hardening.conf > /dev/null <<'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowUsers ops
EOF
# Проверяем синтаксис конфигурации SSH
sudo sshd -t
# Применяем настройки без завершения текущих сессий
sudo systemctl reload ssh
Firewall y fail2ban
Abra SSH solo desde su IP administrativa si es estática. Si la dirección cambia, permita SSH desde una red de confianza o use una VPN. El puerto 8200 no se abre intencionadamente: Caddy accederá a OpenBao de forma local.
# Разрешаем SSH только с административного IPv4
sudo ufw allow from 198.51.100.25 to any port 22 proto tcp
# Открываем публичный HTTPS и временно HTTP для получения сертификата
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Включаем firewall с политикой deny по умолчанию
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
# Проверяем активные правила
sudo ufw status verbose
Active la jail para SSH. Si SSH solo está disponible a través de VPN, indique la subred VPN en la lista de IP de confianza.
# Включаем и запускаем защиту SSH
sudo systemctl enable --now fail2ban
# Проверяем состояние jail sshd
sudo fail2ban-client status sshd
6. Instalación del software — paso a paso
El ejemplo utiliza OpenBao 2.2.x, el servicio del sistema systemd, Caddy 2.10.x y restic 0.18.x. Antes de la instalación en producción, compruebe la versión estable actual en la página oficial de OpenBao y fije una versión concreta. No debe utilizar una URL con la palabra variable latest en un script de actualización automatizado.
Paso 1. Creación del usuario del sistema
# Создаём пользователя без shell для процесса OpenBao
sudo useradd --system --home /etc/openbao --shell /usr/sbin/nologin openbao
# Создаём каталоги конфигурации, данных и TLS
sudo install -d -o openbao -g openbao -m 750 /etc/openbao
sudo install -d -o openbao -g openbao -m 750 /opt/openbao/data
sudo install -d -o openbao -g openbao -m 750 /etc/openbao/tls
Paso 2. Descarga de OpenBao
A continuación se muestra un ejemplo para amd64. Para ARM64, sustituya amd64 por arm64. La versión debe ser la misma en todos los nodos del clúster. Después de la descarga, se recomienda verificar el SHA256 con la suma de comprobación de la versión oficial.
# Фиксируем версию, чтобы обновления были контролируемыми
export OPENBAO_VERSION=2.2.0
# Загружаем официальный архив OpenBao для Linux amd64
curl -fL -o /tmp/openbao.zip \
"https://github.com/openbao/openbao/releases/download/v${OPENBAO_VERSION}/openbao_${OPENBAO_VERSION}_linux_amd64.zip"
# Распаковываем бинарный файл в каталог системных программ
sudo unzip -o /tmp/openbao.zip -d /usr/local/bin
# Назначаем владельца и безопасные права на бинарный файл
sudo chown root:root /usr/local/bin/openbao
sudo chmod 0755 /usr/local/bin/openbao
# Проверяем установленную версию
openbao version
Si la versión utiliza otro patrón de nombre para el archivo, use el nombre exacto del asset de la página de la versión. No sustituya el binario oficial por una compilación de origen desconocido.
Paso 3. Instalación de Caddy
Caddy obtiene y renueva automáticamente los certificados de Let’s Encrypt. Para ello, el nombre DNS debe apuntar a la IPv4 o IPv6 pública del servidor, y los puertos 80 y 443 deben ser accesibles desde Internet.
# Добавляем ключ официального репозитория Caddy
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# Добавляем репозиторий Caddy для Ubuntu
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Устанавливаем Caddy 2.x и проверяем его состояние
sudo apt update
sudo apt install -y caddy
sudo systemctl enable --now caddy
caddy version
Paso 4. Instalación de la unidad systemd
# Создаём службу OpenBao с отдельным пользователем и ограничениями systemd
sudo tee /etc/systemd/system/openbao.service > /dev/null <<'EOF'
[Unit]
Description=OpenBao secrets management server
Documentation=https://openbao.org/docs/
After=network-online.target
Wants=network-online.target
[Service]
User=openbao
Group=openbao
ExecStart=/usr/local/bin/openbao server -config=/etc/openbao/openbao.hcl
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
AmbientCapabilities=CAP_IPC_LOCK
CapabilityBoundingSet=CAP_IPC_LOCK
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/opt/openbao/data
[Install]
WantedBy=multi-user.target
EOF
# Перечитываем unit-файлы systemd
sudo systemctl daemon-reload
Paso 5. Configuración de variables CLI
# Создаём локальный профиль для администратора OpenBao
tee -a ~/.profile > /dev/null <<'EOF'
export BAO_ADDR='https://bao.example.com'
EOF
# Загружаем переменную в текущую оболочку
source ~/.profile
# Проверяем, что CLI установлен и доступен
openbao version
El comando openbao es la CLI principal. En scripts antiguos puede aparecer el comando vault; no mezcle binarios ni variables de entorno sin comprobar la compatibilidad.
7. Configuración
Configuración de OpenBao y Raft
Primero creamos la configuración. OpenBao escucha solo en loopback mediante HTTP, y TLS termina en Caddy. Esta es una opción aceptable porque el tráfico entre los dos procesos no sale del servidor. Si hay una red separada u otro host entre el reverse proxy y OpenBao, TLS interno es obligatorio.
# Создаём конфигурацию OpenBao с Raft Storage и локальным listener
sudo tee /etc/openbao/openbao.hcl > /dev/null <<'EOF'
ui = true
disable_mlock = true
storage "raft" {
path = "/opt/openbao/data"
node_id = "bao-1"
}
listener "tcp" {
address = "127.0.0.1:8200"
cluster_address = "127.0.0.1:8201"
tls_disable = true
}
api_addr = "https://bao.example.com"
cluster_addr = "http://127.0.0.1:8201"
telemetry {
disable_hostname = true
prometheus_retention_time = "24h"
}
EOF
# Назначаем конфигурации владельца и закрытые права
sudo chown openbao:openbao /etc/openbao/openbao.hcl
sudo chmod 640 /etc/openbao/openbao.hcl
# Проверяем конфигурацию без запуска сервера
sudo -u openbao /usr/local/bin/openbao operator validate-config /etc/openbao/openbao.hcl
# Запускаем OpenBao и добавляем его в автозагрузку
sudo systemctl enable --now openbao
# Смотрим последние сообщения службы
sudo systemctl status openbao --no-pager
sudo journalctl -u openbao -n 50 --no-pager
Reverse proxy y HTTPS mediante Caddy
Sustituya el dominio por su propio nombre DNS. La dirección de correo electrónico se puede especificar en el bloque global de Caddy para las notificaciones de certificados. Mientras DNS no apunte al servidor, Caddy no podrá obtener un certificado.
# Настраиваем HTTPS reverse proxy для OpenBao
sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
{
email [email protected]
}
bao.example.com {
encode gzip
reverse_proxy 127.0.0.1:8200 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Content-Type-Options "nosniff"
Referrer-Policy "no-referrer"
}
}
EOF
# Проверяем синтаксис Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
# Перечитываем конфигурацию без остановки Caddy
sudo systemctl reload caddy
# Проверяем HTTPS-заголовки и ответ API
curl -I https://bao.example.com/v1/sys/health
El código de respuesta del health endpoint depende del estado de OpenBao. Para un servidor no inicializado o sellado, a menudo se devuelve HTTP 501 o 503, lo que no indica un fallo del reverse proxy. Es importante ver un certificado TLS correcto y una respuesta JSON de OpenBao.
Inicialización del almacenamiento
La inicialización se realiza una sola vez. El resultado del comando contiene las claves de unseal y el initial root token. No guarde la salida en el historial de shell, chats, tickets ni en un archivo normal del servidor. Ejecute el comando desde un administrador local que disponga de un lugar cifrado para almacenar el resultado.
# Проверяем состояние до инициализации
openbao status
# Инициализируем Raft с пятью ключами и порогом три
openbao operator init -key-shares=5 -key-threshold=3
Guarde las cinco claves con distintos administradores de confianza. A continuación, deselle el servidor proporcionando tres claves de forma interactiva. No pase las claves mediante argumentos de línea de comandos: pueden quedar en el historial o en la lista de procesos.
# Вводим первый unseal key интерактивно
openbao operator unseal
# Повторяем ещё для двух разных ключей
openbao operator unseal
openbao operator unseal
# Проверяем, что хранилище распечатано
openbao status
Autentíquese con el initial root token solo en una sesión administrativa. Después de crear políticas administrativas permanentes, el root token debe guardarse en almacenamiento offline y no utilizarse para aplicaciones.
KV v2 y política de acceso
KV v2 almacena versiones de secretos y admite la lectura, escritura, eliminación y restauración de versiones. Habilitaremos el secrets engine en la ruta secret/, y luego crearemos una aplicación con acceso solo a secret/data/myapp/.
# Экспортируем root token только в текущую оболочку
read -rsp "OpenBao root token: " BAO_TOKEN
export BAO_TOKEN
echo
# Включаем KV v2 на пути secret/
openbao secrets enable -path=secret kv-v2
# Записываем тестовый секрет
openbao kv put secret/myapp/config \
DB_HOST="127.0.0.1" \
DB_NAME="appdb" \
API_ENDPOINT="https://api.example.com"
# Проверяем чтение секрета
openbao kv get secret/myapp/config
Cree un archivo de policy. Para KV v2, la ruta de API contiene data, y la CLI oculta este detalle. El permiso list no permite leer valores, pero solo debe concederse donde la aplicación realmente necesite una lista de claves.
# Создаём минимальную политику для приложения
sudo tee /tmp/myapp-policy.hcl > /dev/null <<'EOF'
path "secret/data/myapp/" {
capabilities = ["read"]
}
path "secret/metadata/myapp/" {
capabilities = ["list"]
}
EOF
# Загружаем policy в OpenBao
openbao policy write myapp-readonly /tmp/myapp-policy.hcl
# Удаляем временный файл с политикой
shred -u /tmp/myapp-policy.hcl
AppRole para la aplicación
AppRole está destinado a la autenticación de máquinas. role_id puede considerarse el identificador del rol, y secret_id el secreto que debe almacenarse en un secreto de deployment protegido. Estableceremos una vida útil corta para el token y limitaremos el número de sus usos.
# Включаем метод аутентификации AppRole
openbao auth enable approle
# Создаём роль с ограниченным TTL и политикой чтения
openbao write auth/approle/role/myapp \
token_policies="myapp-readonly" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=24h \
secret_id_num_uses=2
# Получаем role_id, который можно передать в deployment-систему
openbao read -field=role_id auth/approle/role/myapp/role-id
# Генерируем одноразовый secret_id
openbao write -field=secret_id -f \
auth/approle/role/myapp/secret-id
Proporcione los dos valores a la aplicación mediante un mecanismo seguro de CI/CD o secretos del orquestador. No los añada al Dockerfile, repositorio Git, systemd unit ni a un log público. Después de la prueba, compruebe que la aplicación no pueda leer rutas ajenas.
# Выполняем login через AppRole и получаем временный client_token
export ROLE_ID="replace-with-role-id"
export SECRET_ID="replace-with-secret-id"
export APP_TOKEN="$(
curl -fsS \
--request POST \
--data "{\"role_id\":\"${ROLE_ID}\",\"secret_id\":\"${SECRET_ID}\"}" \
https://bao.example.com/v1/auth/approle/login \
| jq -r '.auth.client_token'
)"
# Читаем разрешённый секрет
curl -fsS \
-H "X-OpenBao-Token: ${APP_TOKEN}" \
https://bao.example.com/v1/secret/data/myapp/config | jq
# Проверяем отказ в доступе к чужому пути
curl -sS -o /tmp/denied.json -w "%{http_code}\n" \
-H "X-OpenBao-Token: ${APP_TOKEN}" \
https://bao.example.com/v1/secret/data/other-app/config
Gestión del root token
Después de verificar AppRole, no utilice el root token para las tareas diarias. Cree una policy administrativa independiente con operaciones limitadas y emita un token personal para cada operador. El root token se puede revocar y, para acciones de emergencia, generar uno nuevo mediante el proceso de recovery de acuerdo con la normativa interna.
# Отзываем root token после завершения первичной настройки
openbao token revoke "$BAO_TOKEN"
# Удаляем токен из текущей оболочки
unset BAO_TOKEN
Verificación de funcionamiento
# Проверяем health endpoint через публичный HTTPS
curl -fsS https://bao.example.com/v1/sys/health | jq
# Проверяем состояние Raft и sealed status
openbao status
# Проверяем, что порт 8200 не слушает внешний адрес
sudo ss -lntp | grep -E ':8200|:443'
# Проверяем состояние обеих служб
systemctl is-active openbao
systemctl is-active caddy
# Смотрим ошибки за последние 15 минут
sudo journalctl -u openbao --since "15 minutes ago" -p warning
sudo journalctl -u caddy --since "15 minutes ago" -p warning
8. Copias de seguridad y mantenimiento
Qué es necesario respaldar
La fuente principal de datos es el snapshot de Raft. Contiene el estado de OpenBao, incluidos los secretos KV, las políticas y la configuración de auth methods. Además, guarde la configuración /etc/openbao/openbao.hcl, la unidad systemd, el Caddyfile y el procedimiento de restauración.
Las claves de unseal y el root token no deben incluirse en un backup habitual del servidor. Deben almacenarse por separado y estar disponibles para varias personas autorizadas. Si cifra el backup con una clave ubicada en el mismo VPS, la pérdida del VPS destruirá tanto los datos como la clave de descifrado.
Creación de un snapshot de Raft
El snapshot se realiza mediante la API y no requiere detener OpenBao. Para el usuario de backup, cree un token con permiso read en el endpoint de snapshot. Este token no debe tener privilegios administrativos.
# Creamos un directorio para el snapshot temporal
sudo install -d -o ops -g ops -m 700 /var/backups/openbao
# Creamos un snapshot de Raft mediante la API local
curl -fsS \
-H "X-OpenBao-Token: ${BAO_BACKUP_TOKEN}" \
https://bao.example.com/v1/sys/storage/raft/snapshot \
-o /var/backups/openbao/raft-$(date -u +%Y%m%dT%H%M%SZ).snap
# Verificamos que el archivo no esté vacío
ls -lh /var/backups/openbao/
El permiso exacto para el endpoint de snapshot depende de la versión de OpenBao y de la ruta utilizada. Consulte la API de políticas de su versión antes de emitir el token. Nunca haga accesible el endpoint de snapshot sin autenticación.
Cifrado de backup mediante restic y S3
Restic cifra los datos antes de enviarlos a S3. Para production, utilice un bucket independiente, access key independientes con permisos mínimos y una contraseña de restic procedente de un gestor de secretos. En el ejemplo, las variables se almacenan en un archivo con permisos 600; este archivo no debe incluirse en Git.
# Creamos un directorio con los parámetros de copia de seguridad
sudo install -d -o root -g root -m 700 /etc/openbao
# Guardamos los parámetros solo para root
sudo tee /etc/openbao/backup.env > /dev/null <<'EOF'
export AWS_ACCESS_KEY_ID='replace-me'
export AWS_SECRET_ACCESS_KEY='replace-me'
export RESTIC_REPOSITORY='s3:https://s3.example.net/openbao-prod'
export RESTIC_PASSWORD='replace-with-long-random-password'
export RESTIC_COMPRESSION='auto'
EOF
# Protegemos el archivo con secretos
sudo chmod 600 /etc/openbao/backup.env
# Inicializamos el repositorio de restic una sola vez
sudo bash -c 'source /etc/openbao/backup.env && restic init'
Para un modelo más estricto, almacene las credentials en systemd credentials, en un almacén de secretos independiente o utilice un rol IAM si el VPS se encuentra en un entorno cloud compatible. No envíe todo el directorio de datos de Raft a un backup externo mediante rsync común: un snapshot coherente es más seguro para la restauración.
Script de backup automático
# Creamos el script para el snapshot de backup y la carga en restic
sudo tee /usr/local/sbin/openbao-backup.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
source /etc/openbao/backup.env
tmpdir="$(mktemp -d /var/backups/openbao.XXXXXX)"
cleanup() {
rm -rf "$tmpdir"
}
trap cleanup EXIT
snapshot="$tmpdir/raft.snap"
metadata="$tmpdir/metadata.txt"
curl --fail --silent --show-error \
-H "X-OpenBao-Token: ${BAO_BACKUP_TOKEN}" \
"https://bao.example.com/v1/sys/storage/raft/snapshot" \
-o "$snapshot"
{
date -u
hostname
openbao version
sha256sum "$snapshot"
} > "$metadata"
restic backup "$snapshot" "$metadata" --tag openbao-raft
restic forget \
--tag openbao-raft \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 12 \
--prune
restic check
EOF
# Permitimos la ejecución solo a root
sudo chmod 700 /usr/local/sbin/openbao-backup.sh
# Añadimos el token de backup al archivo de environment protegido
sudo sh -c 'printf "\nexport BAO_BACKUP_TOKEN='\''replace-with-backup-token'\''\n" >> /etc/openbao/backup.env'
sudo chmod 600 /etc/openbao/backup.env
El comando restic check verifica la integridad de la estructura del repositorio, pero no sustituye una restauración de prueba. Realice la primera ejecución manualmente y asegúrese de que aparezcan objetos cifrados en S3.
systemd timer en lugar de cron
# Creamos una unidad de servicio para el backup
sudo tee /etc/systemd/system/openbao-backup.service > /dev/null <<'EOF'
[Unit]
Description=OpenBao encrypted Raft backup
After=network-online.target openbao.service
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/openbao-backup.sh
User=root
EOF
# Creamos un temporizador para la ejecución diaria a las 03:15 UTC
sudo tee /etc/systemd/system/openbao-backup.timer > /dev/null <<'EOF'
[Unit]
Description=Daily OpenBao backup timer
[Timer]
OnCalendar=-- 03:15:00 UTC
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
EOF
# Habilitamos el temporizador y verificamos la programación
sudo systemctl daemon-reload
sudo systemctl enable --now openbao-backup.timer
sudo systemctl start openbao-backup.service
sudo systemctl list-timers openbao-backup.timer
sudo journalctl -u openbao-backup.service -n 50 --no-pager
Verificación de la restauración
La restauración debe probarse en un VPS temporal independiente o en un contenedor aislado. No se puede comprobar sobre un almacenamiento de production en funcionamiento. Para la restauración se necesita un OpenBao limpio de la misma versión o una compatible, la configuración de Raft y el snapshot.
# Obtenemos el último snapshot de restic en un directorio independiente
sudo mkdir -p /tmp/openbao-restore
sudo restic restore latest --tag openbao-raft \
--target /tmp/openbao-restore
# Verificamos los archivos encontrados
find /tmp/openbao-restore -type f -maxdepth 5 -ls
A continuación, detenga el OpenBao de prueba, sustituya su Raft storage vacío y realice la restauración mediante CLI o API según la documentación de la versión. Después de restaurar, compruebe el unseal, la lectura de un secreto de prueba, el login de AppRole y las políticas. Registre el tiempo real de restauración y corrija el procedimiento si requiere acciones manuales que nadie pueda realizar durante una emergencia.
Actualizaciones
Antes de actualizar, cree un snapshot de Raft reciente y guarde la versión actual. Para un solo nodo, utilice una maintenance window: detenga el servicio, sustituya el archivo binario, inicie OpenBao y compruebe el health endpoint. No elimine el binario anterior hasta completar una verificación satisfactoria.
En un clúster de tres o cinco nodos, la actualización se realiza nodo por nodo con control de quorum. Esto no significa que pueda mezclar cualquier versión sin verificar: consulte las compatibility notes de la versión específica. Después de actualizar un nodo, compruebe el estado de Raft y solo entonces continúe con el siguiente.
# Antes de actualizar, guardamos el estado y el snapshot
openbao status
sudo systemctl start openbao-backup.service
# Tras sustituir el binario, reiniciamos el servicio
sudo systemctl restart openbao
# Verificamos la API, el estado sealed y los últimos errores
curl -fsS https://bao.example.com/v1/sys/health | jq
openbao status
sudo journalctl -u openbao -n 100 --no-pager
9. Troubleshooting y FAQ
OpenBao responde con el error «connection refused» en el puerto 8200
Primero compruebe el estado del proceso con el comando systemctl status openbao y el registro journalctl -u openbao -n 100. A continuación, ejecute ss -lntp | grep 8200: el listener debe estar en 127.0.0.1:8200. Si el service finalizó, la causa suele ser un error de HCL, un propietario incorrecto del directorio Raft o un puerto ocupado. Ejecute openbao operator validate-config y corrija el problema detectado.
HTTPS devuelve 502 Bad Gateway
El código 502 significa que Caddy no puede conectarse al upstream. Asegúrese de que OpenBao esté iniciado y escuche en 127.0.0.1:8200, y de que esa dirección esté especificada en el Caddyfile. Compruebe localmente curl http://127.0.0.1:8200/v1/sys/health y el registro journalctl -u caddy. Después de cambiar la configuración, ejecute caddy validate y luego systemctl reload caddy.
El certificado de Caddy no se emite
Compruebe los registros DNS A y AAAA: ambos deben apuntar al servidor, o elimine el registro AAAA incorrecto. Los puertos 80 y 443 deben estar abiertos en UFW y en el firewall externo del proveedor. Consulte el registro journalctl -u caddy. Let’s Encrypt también limita la frecuencia de las solicitudes, por lo que no debe eliminar y crear repetidamente la configuración durante el diagnóstico.
Después de reiniciar, OpenBao vuelve a estar sealed
Con Shamir seal, este comportamiento es normal: después del inicio se debe introducir el número configurado de claves de unseal. Almacenar automáticamente estas claves en el mismo servidor no es seguro. Introduzca manualmente el número requerido de claves o configure auto-unseal compatible mediante un KMS/HSM externo. Asegúrese de que las claves estén distribuidas entre los administradores y de que el procedimiento de unseal se haya probado antes de una emergencia.
El login de AppRole devuelve 400 o 403
Compruebe que la ruta auth/approle esté habilitada, que role_id pertenezca al rol correcto y que secret_id no haya caducado ni se haya usado el número permitido de veces. Ejecute openbao read auth/approle/role/myapp con un token administrativo y compruebe el TTL. Si el login es correcto, pero la lectura del secreto devuelve 403, revise la policy: para KV v2 se necesita la ruta secret/data/..., no solo secret/....
¿Qué configuración de VPS es mínimamente adecuada?
Para un laboratorio basta con 1 vCPU, 1 GB de RAM y 20 GB de SSD, pero no es la mejor opción para production. Para un sistema de trabajo pequeño, elija como mínimo 2 vCPU, 2–4 GB de RAM, 40 GB de SSD y una IP estática estable. Añada obligatoriamente un backup externo. Si en el mismo VPS se ejecutan CI runners, bases de datos o un reverse proxy para muchos servicios, aumente la memoria a 8 GB o traslade OpenBao a una máquina independiente.
¿Qué elegir para esta tarea: VPS o dedicated?
Para un único nodo de OpenBao y un número reducido de aplicaciones, un VPS suele ser más razonable: los recursos son suficientes y puede escalar añadiendo nodos. Dedicated es necesario cuando se requiere aislamiento físico estricto, alta carga, gran cantidad de servicios vecinos o requisitos de disco predecible. Sin embargo, dedicated por sí solo no proporciona alta disponibilidad. Para HA son más importantes tres nodos Raft y una red correcta entre ellos.
¿Se puede almacenar el snapshot en el mismo VPS?
Una copia local es útil para una recuperación rápida tras una eliminación accidental, pero no protege contra un fallo de disco, la vulneración del servidor o la eliminación del VPS. Utilice el snapshot local como archivo intermedio y después envíelo a un almacenamiento independiente compatible con S3 con cifrado de restic. Conserve varias generaciones de backup y realice periódicamente una restauración de prueba en una máquina independiente.
¿Es necesario abrir el puerto 8200 en Internet?
En el esquema descrito, no: OpenBao escucha únicamente en loopback y el acceso externo se realiza mediante Caddy por HTTPS. Esto reduce la superficie de ataque y simplifica el firewall. El puerto 8200 solo deberá abrirse entre nodos si crea un clúster Raft; en ese caso, limite el acceso mediante una red privada y configure TLS interno. No publique la API directamente sin cifrado ni control de acceso a la red.
¿Cómo saber si el backup realmente funciona?
Compruebe el exit code correcto del servicio systemd, la presencia de un nuevo snapshot en restic y la fecha del último backup. El comando restic snapshots mostrará las generaciones guardadas, y restic check verificará la estructura del repositorio. La comprobación más fiable es restaurar el snapshot en un nodo temporal, realizar unseal de OpenBao, leer un secreto de prueba y verificar el acceso mediante AppRole. Un backup sin un restore comprobado no puede considerarse operativo.
10. Conclusiones y próximos pasos
Como resultado, OpenBao funciona en un VPS independiente con Raft Storage, HTTPS mediante Caddy, autenticación de máquinas AppRole y copias de seguridad remotas cifradas. Los secretos están separados del código fuente, y la aplicación recibe únicamente los permisos necesarios y un token limitado en el tiempo.
Como siguiente paso, cree monitorización del health endpoint, de la vigencia del certificado TLS, del espacio libre y del éxito del backup. Después, configure un clúster Raft de tres nodos si la interrupción de un VPS es inaceptable. Por último, automatice la rotación de secret_id, la restauración de prueba periódica y el procedimiento de actualización de OpenBao mediante una maintenance window.