feat(openbao): hardened same-LAN tape-encryption key store for Kanrisha
A dedicated OpenBao deployment, kept OFF the Kanrisha tape host so a compromise of the tape node can't reach the vault. - Native TLS on the listener (self-signed by default via gen-tls.sh, or a CA-signed cert from a Smallstep CA over ACME) — no Caddy/Let's Encrypt; reached over the LAN, not the public internet. - Integrated raft storage (clean snapshot-based DR). - mlock on (cap_add IPC_LOCK + memlock unlimited + host swapoff in deploy.sh). - Manual unseal by default; optional PKCS#11 HSM auto-unseal. - deploy.sh: Docker install (Alpine/Debian/Alma), self-signed cert, .env seed, swapoff, firewall 8200/tcp, compose up; then prints init/unseal + the KV-v2 + AppRole bootstrap for Kanrisha + the raft-snapshot DR flow. Self-contained (config payload embedded by build.sh). - Registered in automations.sh + the README deployment table. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -110,11 +110,13 @@ deployments/<name>/ # one folder per stack
|
||||
| [`squid`](deployments/squid/) | SSL-bump caching forward proxy — static-content cache + TLS interception via a local CA. **The exception: a forward proxy, not a Caddy/LE site.** | — |
|
||||
| [`copyparty`](deployments/copyparty/) | Portable file server — web UI/WebDAV behind Caddy, plus direct **SFTP** + **FTPS**. Ships a security-notices-aware updater. | — |
|
||||
| [`simplex`](deployments/simplex/) | SimpleX SMP + XFTP relay with Tor hidden services + encrypted backups. | globals/age-pubkey.txt |
|
||||
| [`openbao`](deployments/openbao/) | Hardened tape-encryption key store for [Kanrisha](deployments/kanrisha/). **Exception: native TLS on the LAN, no Caddy/LE.** raft storage, mlock, self-signed or Smallstep-ACME cert, manual/HSM unseal. | globals/age-pubkey.txt (backups) |
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Alpine + Docker Compose + Caddy/Let's Encrypt** across every stack (except
|
||||
`squid`, which is a forward proxy with a local TLS-interception CA — no Caddy/LE).
|
||||
- **Alpine + Docker Compose + Caddy/Let's Encrypt** across every stack, with two
|
||||
exceptions: `squid` (a forward proxy with a local TLS-interception CA) and
|
||||
`openbao` (a same-LAN secrets store with native TLS) — neither uses Caddy/LE.
|
||||
- **`build.sh` → `deploy.sh`**: each stack's `build.sh` embeds its
|
||||
`docker-compose.yml` / `Caddyfile` / `.env.example` into a single
|
||||
self-contained `deploy.sh` (base64 tar.gz). That one file can be `scp`'d to a
|
||||
|
||||
+4
-1
@@ -63,7 +63,7 @@ fi
|
||||
. "$ROOT/scripts/lib.sh"
|
||||
load_globals
|
||||
|
||||
DEPLOYMENTS=(pocket-id beszel headscale webfinger squid copyparty simplex)
|
||||
DEPLOYMENTS=(pocket-id beszel headscale webfinger squid copyparty simplex openbao)
|
||||
SCRIPTS=(setup-host harden-ssh harden-jumphost sshuser auto-update)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -126,6 +126,9 @@ ask_deployment_vars() {
|
||||
ask XFTP_QUOTA "XFTP disk quota" optional
|
||||
ask SSH_PORT "SSH port" optional
|
||||
ask ALLOWED_IP "Your IP to whitelist in sshguard" optional ;;
|
||||
openbao)
|
||||
ask OPENBAO_ADDR "LAN address the Kanrisha tape host reaches the vault at (IP or DNS)"
|
||||
ask OPENBAO_BIND "Host IP to bind the API on (blank = 0.0.0.0)" optional ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Copy to .env and fill in. docker compose picks .env up automatically.
|
||||
# deploy.sh seeds .env from this on first run; an existing .env is never
|
||||
# overwritten. Never commit the populated .env.
|
||||
|
||||
# ─── Reachability ───────────────────────────────────────────────────────────
|
||||
# Address (IP or DNS name) the KANRISHA tape host uses to reach this vault on
|
||||
# the LAN. Seeds the self-signed cert SAN below and is what you point
|
||||
# [encryption.openbao].address at (https://${OPENBAO_ADDR}:8200).
|
||||
OPENBAO_ADDR=10.0.0.10
|
||||
|
||||
# Interface the published API port binds to on the host. Default is all
|
||||
# interfaces; set the LAN IP to avoid listening anywhere else.
|
||||
OPENBAO_BIND=0.0.0.0
|
||||
|
||||
# ─── TLS ────────────────────────────────────────────────────────────────────
|
||||
# Comma-separated SANs for the self-signed cert deploy.sh generates. Include
|
||||
# EVERY name/IP the tape host might use (deploy.sh appends OPENBAO_ADDR + the
|
||||
# loopback for you). To use a CA-signed cert instead (e.g. Smallstep over ACME),
|
||||
# drop tls.crt + tls.key into ./tls and these are ignored — see the README.
|
||||
OPENBAO_TLS_SANS=DNS:localhost,IP:127.0.0.1
|
||||
OPENBAO_TLS_DAYS=825
|
||||
|
||||
# ─── Image tag ──────────────────────────────────────────────────────────────
|
||||
# Pin for reproducible deploys.
|
||||
OPENBAO_TAG=2.5.5
|
||||
|
||||
# ─── Auto-unseal (optional; default is MANUAL unseal) ───────────────────────
|
||||
# Only used when the seal "pkcs11" stanza is enabled in config.hcl (and the HSM
|
||||
# module/device is mounted into the container). Otherwise leave blank and unseal
|
||||
# manually after each restart.
|
||||
OPENBAO_HSM_PIN=
|
||||
@@ -0,0 +1,3 @@
|
||||
# Never commit generated TLS material (the listener key) or a populated .env.
|
||||
/tls/
|
||||
.env
|
||||
@@ -0,0 +1,109 @@
|
||||
# openbao
|
||||
|
||||
Hardened [OpenBao](https://openbao.org) — the tape-encryption key store for
|
||||
[Kanrisha](../kanrisha/). Deliberately **separate from the tape host**: a
|
||||
compromise of the tape node must not reach the vault, and OpenBao's
|
||||
mlock/TLS/unseal lifecycle is cleaner on its own box.
|
||||
|
||||
Unlike the other stacks here there is **no Caddy / Let's Encrypt** — a secrets
|
||||
store terminates TLS itself and is reached over the **LAN**, not the public
|
||||
internet.
|
||||
|
||||
- **Native TLS** on the listener — self-signed by default, or a CA-signed cert
|
||||
from your Smallstep CA over ACME.
|
||||
- **Integrated raft storage** — snapshot-based DR.
|
||||
- **mlock on** — key material never hits swap.
|
||||
- **Manual unseal by default**, or PKCS#11 HSM auto-unseal.
|
||||
|
||||
> ⚠️ This vault is the **sole recovery path for encrypted tapes.** Losing the
|
||||
> OpenBao data *and* the unseal keys/root token loses every encrypted tape. Take
|
||||
> raft snapshots and store the unseal material out of band (below).
|
||||
|
||||
## Required `.env` values
|
||||
|
||||
| Variable | Notes |
|
||||
|----------|-------|
|
||||
| `OPENBAO_ADDR` | IP/DNS the Kanrisha tape host uses to reach this vault (goes in the cert SAN; you point the daemon at `https://$OPENBAO_ADDR:8200`). |
|
||||
| `OPENBAO_BIND` | Host interface the API binds to. Default all; set the LAN IP to narrow it. |
|
||||
| `OPENBAO_TLS_SANS` | Extra SANs for the self-signed cert (deploy.sh adds `OPENBAO_ADDR` + loopback). |
|
||||
| `OPENBAO_TAG` | OpenBao image tag (pin it). |
|
||||
| `OPENBAO_HSM_PIN` | Only for PKCS#11 auto-unseal. Leave blank for manual unseal. |
|
||||
|
||||
See [`.env.example`](.env.example) for the full list.
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
./automations.sh # Deploy on this host → deploy: openbao
|
||||
# or, non-interactive:
|
||||
OPENBAO_ADDR=10.0.0.10 SKIP_PROMPTS=1 bash deployments/openbao/deploy.sh
|
||||
```
|
||||
|
||||
`deploy.sh` installs Docker, generates a self-signed TLS cert (if none present),
|
||||
seeds `.env`, disables swap (for mlock), firewalls `8200/tcp`, and brings the
|
||||
stack up. OpenBao starts **sealed** — initialise + unseal once:
|
||||
|
||||
```bash
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \
|
||||
bao operator init -tls-skip-verify # prints 5 unseal keys + root token
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \
|
||||
bao operator unseal -tls-skip-verify <key> # x3, three different keys
|
||||
```
|
||||
|
||||
**Store the unseal keys + root token out of band** — ideally age-encrypted with
|
||||
your backup recipient (`globals/age-pubkey.txt`), never on this host.
|
||||
|
||||
## TLS
|
||||
|
||||
- **Self-signed (default):** `deploy.sh` runs [`gen-tls.sh`](gen-tls.sh) to
|
||||
create `./tls/tls.{crt,key}` with `OPENBAO_ADDR` in the SAN. Hand `tls.crt` to
|
||||
the Kanrisha daemon as `[encryption.openbao].openbao_ca_cert`.
|
||||
- **Smallstep CA over ACME (option):** issue a cert from your `step-ca` and drop
|
||||
it in `./tls` instead — `gen-tls.sh` then no-ops. e.g. with the `step` client:
|
||||
|
||||
```bash
|
||||
step ca certificate "$OPENBAO_ADDR" ./tls/tls.crt ./tls/tls.key \
|
||||
--provisioner acme --acme https://ca.lan/acme/acme/directory
|
||||
# renew on a timer: step ca renew --daemon ./tls/tls.crt ./tls/tls.key
|
||||
```
|
||||
|
||||
Give the Kanrisha daemon your Smallstep **root** as `openbao_ca_cert` (then it
|
||||
trusts the vault without `-tls-skip-verify`).
|
||||
|
||||
## Auto-unseal (optional)
|
||||
|
||||
Default is manual unseal after each restart. For hands-off restarts, enable the
|
||||
`seal "pkcs11"` stanza in [`config.hcl`](config.hcl), mount the PKCS#11 module +
|
||||
device into the `openbao` service, and set `OPENBAO_HSM_PIN` in `.env`.
|
||||
|
||||
## Bootstrap for Kanrisha
|
||||
|
||||
Once unsealed, enable KV v2 + AppRole and seed the domain key with the Kanrisha
|
||||
bootstrap script (from the Kanrisha repo, `deploy/openbao/bootstrap.sh`):
|
||||
|
||||
```bash
|
||||
BAO_ADDR=https://$OPENBAO_ADDR:8200 BAO_CACERT=./tls/tls.crt \
|
||||
BAO_TOKEN=<root> bash bootstrap.sh
|
||||
```
|
||||
|
||||
It enables the `kanrisha-tape` KV-v2 mount (with **effectively-unlimited**
|
||||
`max_versions` so a key rotation never orphans old tapes), creates the AppRole +
|
||||
policy, seeds the domain key, and prints the `[encryption.openbao]` block for the
|
||||
Kanrisha config. Point the daemon at `address = "https://$OPENBAO_ADDR:8200"`.
|
||||
|
||||
## Backup / DR
|
||||
|
||||
The vault is the sole recovery path for encrypted tapes — back it up:
|
||||
|
||||
```bash
|
||||
# Consistent raft snapshot (safe while running):
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \
|
||||
bao operator raft snapshot save -tls-skip-verify /openbao/data/snapshot.snap
|
||||
# then copy it off-box, age-encrypted:
|
||||
docker compose cp openbao:/openbao/data/snapshot.snap - | \
|
||||
age -r "$(cat ../../globals/age-pubkey.txt)" > openbao-$(date +%F).snap.age
|
||||
```
|
||||
|
||||
Restore: `bao operator raft snapshot restore`. Snapshots do **not** contain the
|
||||
unseal keys — you still need those (and the root token) to unseal a restored
|
||||
vault, which is why they are stored separately.
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build.sh -- (re)embed docker-compose.yml, config.hcl, gen-tls.sh and
|
||||
# .env.example into deploy.sh as a base64-encoded tar.gz payload after
|
||||
# __ARCHIVE_BELOW__. Idempotent: strips any existing payload first.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
SCRIPT="$DIR/deploy.sh"
|
||||
MARKER="__ARCHIVE_BELOW__"
|
||||
|
||||
[[ -f "$SCRIPT" ]] || { echo "deploy.sh not found at $SCRIPT" >&2; exit 1; }
|
||||
for f in docker-compose.yml config.hcl gen-tls.sh .env.example; do
|
||||
[[ -f "$DIR/$f" ]] || { echo "Missing $DIR/$f" >&2; exit 1; }
|
||||
done
|
||||
|
||||
PAYLOAD=$(tar -czf - -C "$DIR" docker-compose.yml config.hcl gen-tls.sh .env.example | base64)
|
||||
|
||||
TMP=$(mktemp)
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
|
||||
sed "/^${MARKER}\$/,\$d" "$SCRIPT" > "$TMP"
|
||||
{
|
||||
echo "$MARKER"
|
||||
echo "$PAYLOAD"
|
||||
} >> "$TMP"
|
||||
|
||||
mv "$TMP" "$SCRIPT"
|
||||
chmod +x "$SCRIPT"
|
||||
trap - EXIT
|
||||
|
||||
size=$(wc -c < "$SCRIPT")
|
||||
echo "Built $SCRIPT (${size} bytes)"
|
||||
@@ -0,0 +1,37 @@
|
||||
#cloud-config
|
||||
#
|
||||
# OpenBao (Kanrisha tape-encryption key store) — harden SSH, then deploy, on a
|
||||
# fresh Alpine/Debian/Alma host on the SAME LAN as the Kanrisha tape host.
|
||||
#
|
||||
# Fill in REPO_URL and the values in the runcmd block, then paste this as the
|
||||
# instance user-data. No public DNS / 80 / 443 needed — this is a LAN vault with
|
||||
# native TLS on 8200. After boot, initialise + unseal once (see the README).
|
||||
|
||||
packages:
|
||||
- git
|
||||
|
||||
runcmd:
|
||||
- hostnamectl set-hostname openbao || true
|
||||
- |
|
||||
set -e
|
||||
REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git
|
||||
REPO_BRANCH=main
|
||||
HARDEN_SSH=1 # harden SSH on this fresh VM (set 0 to skip)
|
||||
SSH_PORT=22
|
||||
ALLOWED_IP= # optional: whitelist your client IP in sshguard
|
||||
git clone --depth 1 --branch "$REPO_BRANCH" "$REPO_URL" /opt/automations
|
||||
cd /opt/automations
|
||||
|
||||
# Harden SSH: PQ KEX, key-only auth, sshguard. Seeds root from
|
||||
# globals/authorized_keys (or SSH_KEYS_URL).
|
||||
if [ "$HARDEN_SSH" = 1 ]; then
|
||||
SSH_PORT="$SSH_PORT" ALLOWED_IP="$ALLOWED_IP" SKIP_PROMPTS=1 FORCE=1 \
|
||||
bash scripts/harden-ssh.sh
|
||||
fi
|
||||
|
||||
# Deploy OpenBao. OPENBAO_ADDR is the LAN address the Kanrisha tape host
|
||||
# reaches this vault at — it goes in the TLS cert SAN. deploy.sh disables
|
||||
# swap (for mlock) and firewalls 8200/tcp.
|
||||
OPENBAO_ADDR=10.0.0.10 \
|
||||
SKIP_PROMPTS=1 \
|
||||
bash deployments/openbao/deploy.sh
|
||||
@@ -0,0 +1,51 @@
|
||||
# OpenBao — hardened, same-LAN tape-encryption key store for Kanrisha.
|
||||
#
|
||||
# Native TLS on the listener (no reverse proxy in front), integrated raft
|
||||
# storage (clean snapshot-based DR), and mlock enabled. This vault is the SOLE
|
||||
# recovery path for encrypted tapes — losing the OpenBao data AND the unseal
|
||||
# material loses every encrypted tape. Back it up (raft snapshots) and store the
|
||||
# unseal keys/root token out of band (see the README + DR runbook).
|
||||
|
||||
storage "raft" {
|
||||
path = "/openbao/data"
|
||||
node_id = "openbao-1"
|
||||
}
|
||||
|
||||
# TLS-terminating listener. tls.crt/tls.key are self-signed by deploy.sh on first
|
||||
# run, or a CA-signed pair you drop in ./tls (e.g. issued by your Smallstep CA
|
||||
# over ACME — see the README). The Kanrisha daemon trusts this via
|
||||
# [encryption.openbao].openbao_ca_cert.
|
||||
listener "tcp" {
|
||||
address = "0.0.0.0:8200"
|
||||
tls_cert_file = "/openbao/tls/tls.crt"
|
||||
tls_key_file = "/openbao/tls/tls.key"
|
||||
# Optional mTLS — require + verify a client cert from the tape host:
|
||||
# tls_require_and_verify_client_cert = true
|
||||
# tls_client_ca_file = "/openbao/tls/client-ca.crt"
|
||||
}
|
||||
|
||||
# mlock keeps key material off swap — REQUIRED. Needs cap_add: [IPC_LOCK] +
|
||||
# ulimits memlock unlimited (docker-compose.yml) and swap disabled on the host.
|
||||
# Do NOT add `disable_mlock = true`.
|
||||
|
||||
# Single-node raft: advertising 127.0.0.1 is fine because a lone node is always
|
||||
# ACTIVE and never redirects clients (they connect straight to the LAN address).
|
||||
# For HA / multiple nodes, set BAO_API_ADDR + BAO_CLUSTER_ADDR to each node's
|
||||
# reachable LAN address in the compose env instead.
|
||||
api_addr = "https://127.0.0.1:8200"
|
||||
cluster_addr = "https://127.0.0.1:8201"
|
||||
ui = false
|
||||
|
||||
# ── Auto-unseal (optional) — DEFAULT IS MANUAL UNSEAL ────────────────────────
|
||||
# With no seal stanza OpenBao starts SEALED and needs `bao operator unseal` (x3)
|
||||
# after every restart. To auto-unseal against a PKCS#11 HSM/token, uncomment and
|
||||
# fill this in, mount the PKCS#11 module + device into the container, and set
|
||||
# OPENBAO_HSM_PIN in .env:
|
||||
#
|
||||
# seal "pkcs11" {
|
||||
# lib = "/usr/lib/softhsm/libsofthsm2.so"
|
||||
# slot = "0"
|
||||
# pin = "env://BAO_HSM_PIN"
|
||||
# key_label = "kanrisha-unseal"
|
||||
# mechanism = "0x1087" # CKM_AES_GCM
|
||||
# }
|
||||
@@ -0,0 +1,315 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# deploy.sh -- deploy the hardened OpenBao tape-encryption key store on a
|
||||
# same-LAN host (Alpine / Debian / Alma). Single-node, runs as root.
|
||||
#
|
||||
# What this does:
|
||||
# 1. Installs docker + compose if missing.
|
||||
# 2. Lays down docker-compose.yml, config.hcl, gen-tls.sh in $STACK_DIR.
|
||||
# 3. Seeds .env on first run (OPENBAO_ADDR into the cert SAN); never
|
||||
# overwrites an existing .env.
|
||||
# 4. Generates a self-signed TLS cert (if ./tls is empty) -- drop a CA-signed
|
||||
# pair there instead to use your Smallstep CA.
|
||||
# 5. Disables swap (so mlock is meaningful) and opens 8200/tcp.
|
||||
# 6. Pulls images and brings the stack up. OpenBao starts SEALED -- initialise
|
||||
# + unseal once afterwards (printed at the end).
|
||||
#
|
||||
# Idempotent: re-run to apply config changes / pull new images.
|
||||
#
|
||||
# Self-contained: docker-compose.yml, config.hcl, gen-tls.sh, .env.example are
|
||||
# embedded as a base64 tar.gz at the bottom. Rebuild with build.sh after edits.
|
||||
#
|
||||
# Usage:
|
||||
# OPENBAO_ADDR=10.0.0.10 bash deploy.sh # interactive prompt for the rest
|
||||
# OPENBAO_ADDR=vault.lan SKIP_PROMPTS=1 bash deploy.sh
|
||||
# STACK_DIR=/opt/openbao bash deploy.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${STACK_DIR:=/srv/openbao}"
|
||||
: "${SKIP_DOCKER_INSTALL:=0}"
|
||||
: "${FORCE:=0}"
|
||||
: "${SKIP_PROMPTS:=0}" # non-interactive: require values via env, no prompts
|
||||
[[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1
|
||||
: "${OPENBAO_ADDR:=}"
|
||||
: "${OPENBAO_BIND:=0.0.0.0}"
|
||||
: "${DISABLE_SWAP:=1}" # set 0 to skip swapoff (mlock then only best-effort)
|
||||
|
||||
log() { printf '\033[1;32m[+]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[[ $EUID -eq 0 ]] || die "Run as root."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# OS detection + Docker install (Alpine / Debian / Alma). Inlined so this
|
||||
# deploy.sh stays self-contained when scp'd standalone.
|
||||
# ---------------------------------------------------------------------------
|
||||
osfam() {
|
||||
local id="" like=""
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
id="$(. /etc/os-release 2>/dev/null && echo "${ID:-}")"
|
||||
like="$(. /etc/os-release 2>/dev/null && echo "${ID_LIKE:-}")"
|
||||
fi
|
||||
case " $id $like " in
|
||||
*" alpine "*) echo alpine ;;
|
||||
*" debian "*|*" ubuntu "*) echo debian ;;
|
||||
*" rhel "*|*" fedora "*|*" centos "*) echo rhel ;;
|
||||
*) echo "${id:-unknown}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_docker() {
|
||||
[[ "$SKIP_DOCKER_INSTALL" == "1" ]] && { log "Skipping Docker install."; return; }
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
log "Docker already installed: $(docker --version)"
|
||||
else
|
||||
log "Installing Docker (OS: $(osfam))..."
|
||||
case "$(osfam)" in
|
||||
alpine) apk add -q docker docker-cli-compose openrc ;;
|
||||
debian|rhel) command -v curl >/dev/null 2>&1 || \
|
||||
{ command -v apt-get >/dev/null 2>&1 && apt-get install -y -qq curl; } || \
|
||||
{ command -v dnf >/dev/null 2>&1 && dnf install -y -q curl; }
|
||||
curl -fsSL https://get.docker.com | sh ;;
|
||||
*) die "Unsupported OS for auto Docker install. Set SKIP_DOCKER_INSTALL=1 and install Docker yourself." ;;
|
||||
esac
|
||||
fi
|
||||
if command -v rc-update >/dev/null 2>&1; then
|
||||
rc-update add docker default >/dev/null 2>&1 || true
|
||||
rc-service docker status >/dev/null 2>&1 || rc-service docker start
|
||||
elif command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl enable --now docker >/dev/null 2>&1 || systemctl start docker || true
|
||||
fi
|
||||
}
|
||||
|
||||
open_bao_port() {
|
||||
# Register 8200/tcp (the vault API). Prefer the host firewall when present;
|
||||
# else ufw/firewalld if active. Restrict the source to the tape host where
|
||||
# you can -- this is a secrets store, not a public service.
|
||||
if [[ -d /etc/firewall/ports.d && -x /usr/local/sbin/firewall-apply ]]; then
|
||||
log "Registering 8200/tcp with host firewall..."
|
||||
printf '8200/tcp\n' > /etc/firewall/ports.d/openbao.rule
|
||||
/usr/local/sbin/firewall-apply
|
||||
elif command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q '^Status: active'; then
|
||||
log "ufw active -- allowing 8200/tcp..."
|
||||
ufw allow 8200/tcp >/dev/null
|
||||
elif command -v firewall-cmd >/dev/null 2>&1 && firewall-cmd --state >/dev/null 2>&1; then
|
||||
log "firewalld active -- allowing 8200/tcp..."
|
||||
firewall-cmd -q --add-port=8200/tcp --permanent
|
||||
firewall-cmd -q --reload
|
||||
fi
|
||||
}
|
||||
|
||||
disable_swap() {
|
||||
[[ "$DISABLE_SWAP" == "1" ]] || { warn "DISABLE_SWAP=0 -- mlock will be best-effort."; return; }
|
||||
if swapon --show 2>/dev/null | grep -q .; then
|
||||
log "Disabling swap (mlock keeps key material off disk)..."
|
||||
swapoff -a || warn "swapoff failed -- disable swap manually."
|
||||
fi
|
||||
# Persist: comment any swap lines in fstab so it stays off across reboots.
|
||||
if [[ -f /etc/fstab ]] && grep -qE '^[^#].*\sswap\s' /etc/fstab; then
|
||||
sed -i.bak -E 's|^([^#].*\sswap\s.*)$|# \1 # disabled for OpenBao mlock|' /etc/fstab
|
||||
log "Commented swap entries in /etc/fstab (backup: /etc/fstab.bak)."
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Extract embedded archive (docker-compose.yml, config.hcl, gen-tls.sh, .env.example)
|
||||
# ----------------------------------------------------------------------------
|
||||
SCRIPT_DIR=$(mktemp -d -t openbao-deploy.XXXXXX)
|
||||
trap 'rm -rf "$SCRIPT_DIR"' EXIT
|
||||
|
||||
extract_archive() {
|
||||
grep -a -A 9999999 '^__ARCHIVE_BELOW__$' "$0" \
|
||||
| tail -n +2 \
|
||||
| base64 -d \
|
||||
| tar -xz -C "$SCRIPT_DIR" 2>/dev/null || true
|
||||
}
|
||||
extract_archive
|
||||
# Fallback: run straight from the source dir (before build.sh embeds a payload).
|
||||
if [[ ! -f "$SCRIPT_DIR/docker-compose.yml" ]]; then
|
||||
SRC=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
for f in docker-compose.yml config.hcl gen-tls.sh .env.example; do
|
||||
[[ -f "$SRC/$f" ]] || die "Missing $f (no embedded payload and not in $SRC -- run build.sh)."
|
||||
cp "$SRC/$f" "$SCRIPT_DIR/$f"
|
||||
done
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Prompt for required values
|
||||
# ----------------------------------------------------------------------------
|
||||
prompt() {
|
||||
local var="$1" msg="$2" cur="${!1}"
|
||||
[[ -n "$cur" ]] && return
|
||||
[[ "$SKIP_PROMPTS" == "1" ]] && die "$var required (set it in the environment; running with SKIP_PROMPTS=1)."
|
||||
read -r -p "$msg: " "$var"
|
||||
}
|
||||
prompt OPENBAO_ADDR "LAN address the Kanrisha tape host reaches this vault at (IP or DNS)"
|
||||
[[ -n "$OPENBAO_ADDR" ]] || die "OPENBAO_ADDR is required."
|
||||
|
||||
# Build the cert SAN list: loopback + whatever OPENBAO_ADDR is (IP vs DNS).
|
||||
if [[ "$OPENBAO_ADDR" =~ ^[0-9.]+$ || "$OPENBAO_ADDR" == *:* ]]; then
|
||||
ADDR_SAN="IP:${OPENBAO_ADDR}"
|
||||
else
|
||||
ADDR_SAN="DNS:${OPENBAO_ADDR}"
|
||||
fi
|
||||
SANS="DNS:localhost,IP:127.0.0.1,${ADDR_SAN}"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Lay down the stack
|
||||
# ----------------------------------------------------------------------------
|
||||
log "Setting up $STACK_DIR..."
|
||||
install -d -m 0750 "$STACK_DIR"
|
||||
install -m 0640 "$SCRIPT_DIR/docker-compose.yml" "$STACK_DIR/docker-compose.yml"
|
||||
install -m 0640 "$SCRIPT_DIR/config.hcl" "$STACK_DIR/config.hcl"
|
||||
install -m 0750 "$SCRIPT_DIR/gen-tls.sh" "$STACK_DIR/gen-tls.sh"
|
||||
|
||||
ENV_FILE="$STACK_DIR/.env"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
log "Seeding $ENV_FILE..."
|
||||
install -m 0600 "$SCRIPT_DIR/.env.example" "$ENV_FILE"
|
||||
sed -i \
|
||||
-e "s|^OPENBAO_ADDR=.*|OPENBAO_ADDR=${OPENBAO_ADDR}|" \
|
||||
-e "s|^OPENBAO_BIND=.*|OPENBAO_BIND=${OPENBAO_BIND}|" \
|
||||
-e "s|^OPENBAO_TLS_SANS=.*|OPENBAO_TLS_SANS=${SANS}|" \
|
||||
"$ENV_FILE"
|
||||
else
|
||||
log ".env exists; leaving it alone."
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# TLS: self-signed unless a cert is already present (drop in a CA-signed pair
|
||||
# to use your Smallstep CA -- see the README).
|
||||
# ----------------------------------------------------------------------------
|
||||
install -d -m 0750 "$STACK_DIR/tls"
|
||||
OPENBAO_TLS_SANS="$SANS" bash "$STACK_DIR/gen-tls.sh" "$STACK_DIR/tls"
|
||||
|
||||
disable_swap
|
||||
open_bao_port
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Bring up the stack
|
||||
# ----------------------------------------------------------------------------
|
||||
if [[ "$FORCE" != "1" ]]; then
|
||||
printf '\nAbout to start OpenBao from %s (TLS on https://%s:8200). Continue? [y/N] ' "$STACK_DIR" "$OPENBAO_ADDR"
|
||||
read -r ans
|
||||
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]] || { warn "Aborted."; exit 0; }
|
||||
fi
|
||||
|
||||
cd "$STACK_DIR"
|
||||
log "Pulling image..."
|
||||
docker compose pull
|
||||
log "Starting OpenBao..."
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
# OpenBao starts SEALED (and, first time, uninitialised), so it reports unhealthy
|
||||
# until you init + unseal -- that is expected. Give it a moment to bind.
|
||||
sleep 5
|
||||
docker compose ps
|
||||
|
||||
cat <<EOF
|
||||
|
||||
================================================================
|
||||
DEPLOYED (OpenBao starts SEALED -- finish setup below)
|
||||
|
||||
Address: https://${OPENBAO_ADDR}:8200
|
||||
Stack dir: ${STACK_DIR}
|
||||
TLS: ${STACK_DIR}/tls/tls.crt (give this to the Kanrisha daemon as
|
||||
[encryption.openbao].openbao_ca_cert)
|
||||
|
||||
1. Initialise + unseal (ONCE). Store the unseal keys + root token OUT OF BAND:
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \\
|
||||
bao operator init -tls-skip-verify
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \\
|
||||
bao operator unseal -tls-skip-verify <key> # x3, three different keys
|
||||
|
||||
2. Bootstrap for Kanrisha (from the Kanrisha repo's deploy/openbao/bootstrap.sh):
|
||||
BAO_ADDR=https://${OPENBAO_ADDR}:8200 BAO_CACERT=${STACK_DIR}/tls/tls.crt \\
|
||||
BAO_TOKEN=<root> bash bootstrap.sh
|
||||
Then point the Kanrisha daemon at:
|
||||
[encryption.openbao]
|
||||
address = "https://${OPENBAO_ADDR}:8200"
|
||||
openbao_ca_cert = "/etc/kanrisha/openbao-ca.crt" # = tls.crt above
|
||||
|
||||
3. Back it up (this vault is the sole recovery path for encrypted tapes):
|
||||
docker compose exec -e BAO_ADDR=https://127.0.0.1:8200 openbao \\
|
||||
bao operator raft snapshot save -tls-skip-verify /openbao/data/snap.snap
|
||||
...copy it off-box, age-encrypted. See the README for the full DR flow.
|
||||
|
||||
Manage:
|
||||
docker compose logs -f
|
||||
docker compose pull && docker compose up -d # update
|
||||
docker compose down # stop, keep the vault data
|
||||
docker compose down -v # stop, WIPE the vault (DESTROYS keys)
|
||||
|
||||
Re-running this script is idempotent (it won't re-init or touch .env / tls).
|
||||
================================================================
|
||||
EOF
|
||||
|
||||
# IMPORTANT: nothing executable below this line. Everything after
|
||||
# __ARCHIVE_BELOW__ is the embedded tar.gz payload (base64), added by build.sh.
|
||||
exit 0
|
||||
__ARCHIVE_BELOW__
|
||||
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
|
||||
pZZaaqmlllpqqaWWWmr5/5N/AAPPvDEAUAAA
|
||||
@@ -0,0 +1,50 @@
|
||||
# OpenBao stack — hardened tape-encryption key store for Kanrisha.
|
||||
#
|
||||
# Topology (same-LAN, NO reverse proxy):
|
||||
# Kanrisha tape host --https--> openbao:8200 (native TLS on the vault itself)
|
||||
#
|
||||
# Unlike the other stacks in this repo there is no Caddy / Let's Encrypt: a
|
||||
# secrets store terminates TLS itself and is reached over the LAN, not the
|
||||
# public internet. TLS is a self-signed cert (deploy.sh generates it) or a
|
||||
# CA-signed pair you drop in ./tls (e.g. via your Smallstep CA over ACME).
|
||||
|
||||
name: openbao
|
||||
|
||||
volumes:
|
||||
openbao-data: # raft integrated storage — the vault's persistent state
|
||||
|
||||
services:
|
||||
openbao:
|
||||
image: openbao/openbao:${OPENBAO_TAG:-2.5.5}
|
||||
container_name: openbao
|
||||
command: server -config=/openbao/config/config.hcl
|
||||
restart: unless-stopped
|
||||
# mlock: keep key material off swap. IPC_LOCK + unlimited memlock let the
|
||||
# process lock its memory; the HOST must also have swap disabled.
|
||||
cap_add:
|
||||
- IPC_LOCK
|
||||
ulimits:
|
||||
memlock: -1
|
||||
ports:
|
||||
# Native-TLS API. Bind to the LAN IP (OPENBAO_BIND) to avoid exposing it on
|
||||
# every interface; firewall it to the tape host (deploy.sh does this).
|
||||
- "${OPENBAO_BIND:-0.0.0.0}:8200:8200"
|
||||
volumes:
|
||||
- ./config.hcl:/openbao/config/config.hcl:ro
|
||||
- ./tls:/openbao/tls:ro
|
||||
- openbao-data:/openbao/data
|
||||
environment:
|
||||
# PKCS#11 auto-unseal PIN — only read when the seal "pkcs11" stanza is
|
||||
# enabled in config.hcl. Harmless (unused) otherwise.
|
||||
BAO_HSM_PIN: "${OPENBAO_HSM_PIN:-}"
|
||||
# HA / multi-node only: advertise this node's reachable LAN address.
|
||||
# BAO_API_ADDR: "https://${OPENBAO_ADDR}:8200"
|
||||
# BAO_CLUSTER_ADDR: "https://${OPENBAO_ADDR}:8201"
|
||||
# 0 = unsealed (healthy); 2 = sealed -> unhealthy so a node needing an unseal
|
||||
# is visible. -tls-skip-verify because the listener may use a self-signed cert.
|
||||
healthcheck:
|
||||
test: ["CMD", "bao", "status", "-address=https://127.0.0.1:8200", "-tls-skip-verify"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# gen-tls.sh — generate a self-signed TLS cert + key for the OpenBao listener,
|
||||
# if one is not already present. Never overwrites existing files, so to use a
|
||||
# CA-signed cert (e.g. issued by your Smallstep CA over ACME) just drop tls.crt +
|
||||
# tls.key into the target dir instead and this becomes a no-op.
|
||||
#
|
||||
# Usage: bash gen-tls.sh [TLS_DIR] (default ./tls)
|
||||
# Env: OPENBAO_TLS_SANS comma SAN list, e.g. "DNS:vault.lan,IP:10.0.0.10,IP:127.0.0.1"
|
||||
# OPENBAO_TLS_DAYS validity in days (default 825 — the max browsers accept)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DIR="${1:-./tls}"
|
||||
SANS="${OPENBAO_TLS_SANS:-DNS:localhost,IP:127.0.0.1}"
|
||||
DAYS="${OPENBAO_TLS_DAYS:-825}"
|
||||
|
||||
mkdir -p "$DIR"
|
||||
|
||||
if [[ -f "$DIR/tls.crt" && -f "$DIR/tls.key" ]]; then
|
||||
echo "[gen-tls] TLS material already present in $DIR — leaving it alone."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
command -v openssl >/dev/null 2>&1 || { echo "[gen-tls] openssl not found" >&2; exit 1; }
|
||||
|
||||
# ECDSA P-256 self-signed cert. The SAN must include every address the tape host
|
||||
# (and CLI) use to reach the vault, or their TLS verification fails.
|
||||
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
|
||||
-keyout "$DIR/tls.key" -out "$DIR/tls.crt" -days "$DAYS" \
|
||||
-subj "/CN=openbao" -addext "subjectAltName=${SANS}"
|
||||
|
||||
chmod 600 "$DIR/tls.key"
|
||||
chmod 644 "$DIR/tls.crt"
|
||||
|
||||
echo "[gen-tls] Generated self-signed OpenBao cert in $DIR"
|
||||
echo "[gen-tls] SANs: ${SANS} validity: ${DAYS}d"
|
||||
echo "[gen-tls] Give $DIR/tls.crt to the Kanrisha daemon as"
|
||||
echo "[gen-tls] [encryption.openbao].openbao_ca_cert (so it trusts this listener)."
|
||||
Reference in New Issue
Block a user