Vaultwarden — bootstrap y gestión de secretos¶
Procedimientos para poner en marcha Vaultwarden y gestionar los secretos del cluster a través de VKS.
Bootstrap inicial¶
Antes de exponer el servicio, se accede por port-forward:
kubectl -n vaultwarden port-forward svc/vaultwarden 8080:80
1. Crear el usuario admin¶
En http://localhost:8080, crear la cuenta gorkarevilla@gmail.com con una
master password fuerte. Será el owner de la organización.
2. Verificar el panel /admin¶
En http://localhost:8080/admin, entrar con el ADMIN_TOKEN (SealedSecret
vaultwarden-admin-token; el valor en claro está en el .env local como
VAULTWARDEN_ADMIN_TOKEN).
3. Crear la organización "Familia"¶
En la web vault del usuario admin: crear organización Familia.
4. Crear las colecciones¶
Dentro de Familia, crear 2 colecciones: Familia (compartida) y Homelab.
Las cajas fuertes personales de Gorka y Miren ya sirven como "secretos de Gorka" y "secretos de Miren"; no necesitan colección propia.
5. Invitar a Miren¶
Invitar a miren (email de Miren) con acceso a la colección Familia. La
invitación sale por el SMTP configurado.
6. Crear el usuario de servicio vaultwarden-sync¶
Crear un usuario de servicio con email gorkarevilla+vaultwarden-sync@gmail.com
(alias de Gmail: así el correo de verificación/invitación llega a tu inbox) y
master password generada (guardarla en el .env como VKS_MASTER_PASSWORD).
Invitarlo a Familia con acceso solo lectura a Homelab.
La colección debe llamarse exactamente
Homelab(VKS está configurado conVAULTWARDEN__COLLECTIONNAME=Homelab).
7. Generar la API key de vaultwarden-sync¶
En la cuenta de vaultwarden-sync (Settings → Security → Keys → API key),
generar la API key. Guardar en el .env:
VKS_BW_CLIENTID=<client_id>
VKS_BW_CLIENTSECRET=<client_secret>
La API key debe ser de usuario, no de organización (la API de Vaultwarden no soporta las de organización).
8. Sellar las credenciales de VKS¶
kubectl create secret generic vaultwarden-kubernetes-secrets \
--namespace vaultwarden-kubernetes-secrets \
--from-literal=BW_CLIENTID="$VKS_BW_CLIENTID" \
--from-literal=BW_CLIENTSECRET="$VKS_BW_CLIENTSECRET" \
--from-literal=VAULTWARDEN__MASTERPASSWORD="$VKS_MASTER_PASSWORD" \
--dry-run=client -o yaml \
| kubeseal --controller-name sealed-secrets \
--controller-namespace kube-system --format yaml \
> clusters/luka/config/vaultwarden-kubernetes-secrets/sync-creds-sealed.yaml
9. Desactivar el registro abierto¶
Una vez creados el admin y los usuarios (y antes de exponer externamente),
desactivar el registro abierto: cambiar SIGNUPS_ALLOWED a "false" en
clusters/luka/config/vaultwarden/deployment.yaml (commit separado). Durante el
bootstrap estaba "true" para poder crear la primera cuenta por la web (solo
accesible vía port-forward, no expuesta).
Añadir un secreto de cluster¶
Un secreto que debe llegar a k8s es un ítem de Vaultwarden en la colección
Homelab con el campo custom namespaces (separado por comas si son varios):
- Login → produce un
Secretcon clavesusernameypassword(o las que indiques consecret-key-username/secret-key-password). - Secure Note con YAML válido en
notes→ se aplica tal cual (útil para claves arbitrarias comopgpass).
Campos custom útiles: namespaces, secret-name, secret-type,
secret-key-username, secret-key-password, secret-annotation, secret-label.
VKS sincroniza cada 5 minutos (o al reiniciar el pod para forzar).
Ejemplo¶
Ítem Login test password con campo namespaces=default → crea el Secret
test-password (el nombre sale del nombre del ítem, espacios → -) en el
namespace default, con claves username y password.
Gotchas¶
- El campo custom se llama
namespaces(plural).namespace(singular) no se reconoce y el ítem se ignora. - El nombre del
Secretse sanea desde el nombre del ítem (espacios y caracteres especiales →-). Para un nombre exacto, usa el camposecret-name. - Verificar:
kubectl get secret <nombre> -n <namespace>.
Importar claves en bloque (Bitwarden JSON)¶
Para pasar varias claves de una vez (p. ej. al migrar), mejor que crear ítems a
mano: se genera un fichero JSON de importación de Bitwarden con los valores del
.env y se importa en la colección Homelab.
El fichero es vaultwarden-import.json (gitignored; contiene secretos).
Formato de cada ítem¶
{
"type": 1,
"name": "cloudflare-api-token",
"login": { "username": null, "password": "<valor>" },
"fields": [
{ "name": "namespaces", "value": "cert-manager", "type": 0 },
{ "name": "secret-name", "value": "cloudflare-api-token", "type": 0 },
{ "name": "secret-key-password", "value": "api-token", "type": 0 }
]
}
- Ítems de cluster (se sincronizan):
type: 1(Login) con los campos customnamespaces,secret-name,secret-key-password/secret-key-username(tipo0= text). - Ítems de referencia (CI/Terraform, no se sincronizan): mismo formato sin
namespaces→ VKS los ignora. Sirven para tenerlos a mano en Vaultwarden.
Importar¶
- Org
Familia→ Tools → Import data. - Formato
Bitwarden (json). - Destino: colección
Homelab. - Subir
vaultwarden-import.json.
⚠️ Importar en la colección
Homelab(no en la caja fuerte personal), si no VKS no ve los ítems.
Generar el fichero¶
Un script Python lee el .env y escribe el JSON (patrón repetible):
set -a; source .env; set +a
python3 - <<'EOF'
import json, os
def login(u=None, p=None, uri=None):
return {"username": u, "password": p, "uris": [{"uri": uri}] if uri else [], "totp": None}
def f(n, v): return {"name": n, "value": v, "type": 0}
items = [
{"type": 1, "name": "cloudflare-api-token",
"login": login(password=os.environ["CERT_MANAGER_CLOUDFLARE_TOKEN"]),
"fields": [f("namespaces", "cert-manager"), f("secret-name", "cloudflare-api-token"), f("secret-key-password", "api-token")]},
]
json.dump({"encrypted": False, "folders": [], "items": items}, open("vaultwarden-import.json", "w"), indent=2, ensure_ascii=False)
EOF
Migrar un servicio del legacy al cluster (corte/rollback)¶
El corte de un servicio expuesto (p. ej. heimdall) es un flip de CNAME:
- Añadir el hostname al túnel del cluster (Terraform) y su HTTPRoute en Traefik.
- Cambiar el CNAME de
heimdall.gorkarevilla.comdehometunnel.gorkarevilla.comahomelabtunnel.gorkarevilla.com. - Rollback: volver a apuntar el CNAME a
hometunnel.gorkarevilla.com.
Con Cloudflare proxied (TTL automático) el cambio es casi instantáneo.