infra: add local vault service scaffold for fregat
This commit is contained in:
@@ -5,6 +5,7 @@ version = 1
|
|||||||
"web-frontend" = { deploy_mode = "dokploy_webhook", server = "main" }
|
"web-frontend" = { deploy_mode = "dokploy_webhook", server = "main" }
|
||||||
"manager-frontend" = { deploy_mode = "dokploy_webhook", server = "main" }
|
"manager-frontend" = { deploy_mode = "dokploy_webhook", server = "main" }
|
||||||
"hatchet-worker" = { deploy_mode = "dokploy_webhook", server = "main" }
|
"hatchet-worker" = { deploy_mode = "dokploy_webhook", server = "main" }
|
||||||
|
"vault" = { deploy_mode = "dokploy_webhook", server = "main" }
|
||||||
|
|
||||||
[servers]
|
[servers]
|
||||||
"main" = { tailscale_user = "root", tailscale_name = "main-prod" }
|
"main" = { tailscale_user = "root", tailscale_name = "main-prod" }
|
||||||
|
|||||||
2
vault/.dockerignore
Normal file
2
vault/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
backups/
|
||||||
|
*.json
|
||||||
11
vault/Dockerfile
Normal file
11
vault/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM hashicorp/vault:1.21.3
|
||||||
|
|
||||||
|
COPY config /vault/config
|
||||||
|
COPY entrypoint.sh /vault/entrypoint.sh
|
||||||
|
RUN chmod +x /vault/entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 8200 8201
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD VAULT_ADDR=http://127.0.0.1:8200 vault status >/dev/null 2>&1 || exit 1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/vault/entrypoint.sh"]
|
||||||
85
vault/README.md
Normal file
85
vault/README.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Vault Setup (Dokploy)
|
||||||
|
|
||||||
|
This folder is intended for Dokploy deployment via `Dockerfile` (not docker-compose).
|
||||||
|
|
||||||
|
## Runtime
|
||||||
|
|
||||||
|
Container image uses `vault/config/vault.hcl` and starts through `/vault/entrypoint.sh`.
|
||||||
|
|
||||||
|
Required runtime settings in Dokploy:
|
||||||
|
|
||||||
|
- add capability: `IPC_LOCK`
|
||||||
|
- mount persistent volume to `/vault/data` (mandatory)
|
||||||
|
- expose port `8200` (API)
|
||||||
|
- optionally expose `8201` (cluster)
|
||||||
|
- service update strategy: `stop-first` (important for single-node raft volume)
|
||||||
|
|
||||||
|
## Auto-Unseal Strategy In This Project
|
||||||
|
|
||||||
|
This project uses Shamir + startup unseal in entrypoint:
|
||||||
|
|
||||||
|
- Vault starts normally with Shamir seal
|
||||||
|
- if Vault is sealed, entrypoint runs `vault operator unseal`
|
||||||
|
- unseal key is taken from environment variable
|
||||||
|
|
||||||
|
Supported env vars:
|
||||||
|
|
||||||
|
- `VAULT_UNSEAL_KEY` for single key
|
||||||
|
- `VAULT_UNSEAL_KEYS` for multiple keys (comma/space separated)
|
||||||
|
|
||||||
|
If Vault is sealed and these vars are empty, container exits with error.
|
||||||
|
|
||||||
|
## Recommended Init For This Flow
|
||||||
|
|
||||||
|
To make restart-unseal automatic with one env var, initialize Vault with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vault operator init -key-shares=1 -key-threshold=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Then put generated unseal key into Dokploy env:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VAULT_UNSEAL_KEY=<generated-unseal-key>
|
||||||
|
```
|
||||||
|
|
||||||
|
If Vault was initialized with threshold > 1, set all required keys in:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VAULT_UNSEAL_KEYS=<key1>,<key2>,<key3>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Checks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export VAULT_ADDR=http://127.0.0.1:8200
|
||||||
|
vault status
|
||||||
|
```
|
||||||
|
|
||||||
|
If it is sealed after restart:
|
||||||
|
|
||||||
|
- verify `VAULT_UNSEAL_KEY` / `VAULT_UNSEAL_KEYS` exists in Dokploy
|
||||||
|
- verify persistent volume is still mounted to `/vault/data`
|
||||||
|
- verify container logs from `/vault/entrypoint.sh`
|
||||||
|
|
||||||
|
## Native Vault Auto-Unseal (Optional)
|
||||||
|
|
||||||
|
If later you want native Vault auto-unseal (without storing unseal key in env), use seal providers:
|
||||||
|
|
||||||
|
- [AWS KMS seal](https://developer.hashicorp.com/vault/docs/configuration/seal/awskms)
|
||||||
|
- [GCP KMS seal](https://developer.hashicorp.com/vault/docs/configuration/seal/gcpckms)
|
||||||
|
- [Azure Key Vault seal](https://developer.hashicorp.com/vault/docs/configuration/seal/azurekeyvault)
|
||||||
|
- [Transit seal](https://developer.hashicorp.com/vault/docs/configuration/seal/transit)
|
||||||
|
|
||||||
|
## KV Layout
|
||||||
|
|
||||||
|
Vault stores environment variables in KV v2 under:
|
||||||
|
|
||||||
|
- `secret/shared/<env>`
|
||||||
|
- `secret/projects/<project>/<env>`
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- `secret/shared/prod`
|
||||||
|
- `secret/projects/web-backend/prod`
|
||||||
|
- `secret/projects/brand-center-backend/staging`
|
||||||
16
vault/config/vault.hcl
Normal file
16
vault/config/vault.hcl
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
ui = true
|
||||||
|
disable_mlock = true
|
||||||
|
|
||||||
|
storage "raft" {
|
||||||
|
path = "/vault/data"
|
||||||
|
node_id = "vault-1"
|
||||||
|
}
|
||||||
|
|
||||||
|
listener "tcp" {
|
||||||
|
address = "0.0.0.0:8200"
|
||||||
|
cluster_address = "0.0.0.0:8201"
|
||||||
|
tls_disable = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster_addr = "http://127.0.0.1:8201"
|
||||||
|
api_addr = "http://0.0.0.0:8200"
|
||||||
67
vault/entrypoint.sh
Executable file
67
vault/entrypoint.sh
Executable file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
export VAULT_ADDR="http://127.0.0.1:8200"
|
||||||
|
|
||||||
|
get_status_json() {
|
||||||
|
vault status -format=json 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
read_flag() {
|
||||||
|
json="$1"
|
||||||
|
key="$2"
|
||||||
|
printf '%s' "$json" | tr -d '\n' | sed -n "s/.*\"$key\":[[:space:]]*\\(true\\|false\\).*/\\1/p" | head -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
vault server -config=/vault/config/vault.hcl &
|
||||||
|
VAULT_PID=$!
|
||||||
|
|
||||||
|
echo "Waiting for Vault status endpoint..."
|
||||||
|
while true; do
|
||||||
|
STATUS_JSON="$(get_status_json)"
|
||||||
|
INIT_FLAG="$(read_flag "$STATUS_JSON" initialized)"
|
||||||
|
SEALED_FLAG="$(read_flag "$STATUS_JSON" sealed)"
|
||||||
|
if [ -n "$INIT_FLAG" ] && [ -n "$SEALED_FLAG" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$INIT_FLAG" = "false" ]; then
|
||||||
|
echo "Vault is not initialized yet; auto-unseal skipped."
|
||||||
|
wait $VAULT_PID
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SEALED_FLAG" = "true" ]; then
|
||||||
|
UNSEAL_KEYS_RAW="${VAULT_UNSEAL_KEYS:-${VAULT_UNSEAL_KEY:-}}"
|
||||||
|
if [ -z "$UNSEAL_KEYS_RAW" ]; then
|
||||||
|
echo "Vault is sealed but VAULT_UNSEAL_KEY/VAULT_UNSEAL_KEYS is empty."
|
||||||
|
kill $VAULT_PID || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Vault is sealed; applying unseal keys from environment..."
|
||||||
|
for key in $(printf '%s' "$UNSEAL_KEYS_RAW" | tr ',;' ' '); do
|
||||||
|
[ -n "$key" ] || continue
|
||||||
|
vault operator unseal "$key" >/dev/null
|
||||||
|
STATUS_JSON="$(get_status_json)"
|
||||||
|
SEALED_FLAG="$(read_flag "$STATUS_JSON" sealed)"
|
||||||
|
if [ "$SEALED_FLAG" = "false" ]; then
|
||||||
|
echo "Vault unsealed."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
STATUS_JSON="$(get_status_json)"
|
||||||
|
SEALED_FLAG="$(read_flag "$STATUS_JSON" sealed)"
|
||||||
|
if [ "$SEALED_FLAG" != "false" ]; then
|
||||||
|
echo "Vault is still sealed after provided key(s)."
|
||||||
|
kill $VAULT_PID || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Vault is already unsealed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
wait $VAULT_PID
|
||||||
Reference in New Issue
Block a user