From 530e7dce2ac548ee748a8c1e0ffc11ecca64b5b2 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:25:39 +0200 Subject: [PATCH] Add Tailscale reverse proxy support to OpenBao LXC --- openbao/README.md | 40 ++++++++++++++++++-- openbao/install.sh | 92 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/openbao/README.md b/openbao/README.md index f1aefa6..6166b33 100644 --- a/openbao/README.md +++ b/openbao/README.md @@ -52,6 +52,9 @@ Every parameter is exposed as an environment variable: | `BRIDGE` | `vmbr0` | Network bridge | | `LXC_TAG` | `openbao` | Stable tag used to re-discover the container | | `OPENBAO_VERSION` | `latest` | Pin a specific release (e.g. `v2.0.3`) or `latest` | +| `OPENBAO_LISTEN_ADDR` | `127.0.0.1:8200` | TCP listener address. Loopback by default — Tailscale fronts it. | +| `OPENBAO_API_ADDR` | `http://` | Public API URL (used for UI / OIDC redirects). Set to `https://..ts.net` once known. | +| `TS_AUTHKEY` | _(unset)_ | Pre-auth key (generate at ). If unset, finish `tailscale up` manually inside the LXC. | ```bash CTID=210 OPENBAO_HOSTNAME=vault CORES=4 RAM=2048 \ @@ -69,6 +72,36 @@ bao operator init # save the unseal keys + root token somewhere safe bao operator unseal # repeat with each key share until unsealed ``` +#### Tailscale reverse proxy + +The listener binds to `127.0.0.1:8200` only — Tailscale (running inside the +same LXC) acts as the reverse proxy and terminates TLS via tailnet +certificates. + +If `TS_AUTHKEY` was supplied at install time, the script runs +`tailscale up` and `tailscale serve --bg --https=443 http://127.0.0.1:8200` +automatically. OpenBao then becomes reachable at +`https://..ts.net`. + +Otherwise, finish setup manually inside the LXC: + +```bash +pct enter +tailscale up --ssh +tailscale serve --bg --https=443 http://127.0.0.1:8200 +tailscale status # prints the tailnet FQDN +``` + +Then point `OPENBAO_API_ADDR` at that FQDN and rerun the script so the UI / OIDC redirects use it: + +```bash +OPENBAO_API_ADDR=https://openbao..ts.net \ + bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh)" +``` + +> HTTPS in `tailscale serve` requires HTTPS to be enabled on your tailnet +> (Admin console → DNS → HTTPS Certificates). + #### Update (from inside the LXC) ```bash @@ -81,9 +114,10 @@ is kept as `bao.bak.`), then the service is restarted. ### Architecture -- **OS**: latest Alpine LXC template (auto-detected), unprivileged, `nesting=1` +- **OS**: latest Alpine LXC template (auto-detected), unprivileged, `nesting=1`, `/dev/net/tun` passthrough for Tailscale - **Binary**: official `bao` release from `github.com/openbao/openbao`, installed in `/usr/local/bin` - **Service**: OpenRC, runs as user `openbao`, logs to `/var/log/openbao.log` (rotated daily, 7 days retained) -- **Config**: `/etc/openbao/config.hcl` — raft storage, TLS disabled (terminate TLS at the reverse proxy), `disable_mlock = true` for unprivileged LXC -- **Data**: `/var/lib/openbao/data` (raft) +- **Network**: listener bound to `127.0.0.1:8200`; **Tailscale** runs in the LXC and acts as the reverse proxy (`tailscale serve --https=443`) +- **Config**: `/etc/openbao/config.hcl` — raft storage, TLS disabled on the listener (Tailscale terminates TLS), `disable_mlock = true` for unprivileged LXC +- **Data**: `/var/lib/openbao/data` (raft) - **Version tracking**: `/opt/openbao_version.txt` records the currently installed tag for idempotent reruns diff --git a/openbao/install.sh b/openbao/install.sh index 2350c03..069c749 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -27,6 +27,14 @@ BRIDGE="${BRIDGE:-vmbr0}" LXC_TAG="${LXC_TAG:-openbao}" # stable identifier for the container OPENBAO_VERSION="${OPENBAO_VERSION:-latest}" # "latest" or e.g. "v2.0.3" OPENBAO_API="https://api.github.com/repos/openbao/openbao/releases" +OPENBAO_LISTEN_ADDR="${OPENBAO_LISTEN_ADDR:-127.0.0.1:8200}" +# Public API address advertised to clients (also used for OIDC / UI redirects). +# Defaults to the local listener; override with the tailnet URL once known, +# e.g. OPENBAO_API_ADDR="https://openbao..ts.net". +OPENBAO_API_ADDR="${OPENBAO_API_ADDR:-http://${OPENBAO_LISTEN_ADDR}}" +# Optional: pre-authorise the LXC's Tailscale non-interactively. +# Generate at https://login.tailscale.com/admin/settings/keys +TS_AUTHKEY="${TS_AUTHKEY:-}" SCRIPT_URL="https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh" VERSION_FILE="/opt/openbao_version.txt" BAO_USER="openbao" @@ -136,6 +144,53 @@ install_or_upgrade_bao() { fi } +# ============================================================ +# Reusable: bring Tailscale up and publish OpenBao on the tailnet. +# Idempotent: re-running is a no-op once Tailscale is logged in and the +# serve mapping is already in place. +# ============================================================ +configure_tailscale_proxy() { + if ! command -v tailscale >/dev/null 2>&1; then + log_warn "tailscale CLI not found, skipping reverse-proxy setup." + return 0 + fi + + # 1. Authenticate the node (if it isn't already). + local backend_state + backend_state=$(tailscale status --json 2>/dev/null | jq -r '.BackendState // "unknown"') + if [[ "$backend_state" != "Running" ]]; then + if [[ -n "$TS_AUTHKEY" ]]; then + log_info "Bringing Tailscale up with provided auth key..." + tailscale up --authkey "$TS_AUTHKEY" --ssh --hostname "$HOSTNAME_LXC" \ + || log_warn "tailscale up failed — run it manually inside the LXC." + else + log_warn "Tailscale not authenticated and TS_AUTHKEY was not supplied." + log_warn "Finish setup inside the LXC with: tailscale up --ssh" + log_warn "Then publish OpenBao with: tailscale serve --bg --https=443 http://${OPENBAO_LISTEN_ADDR}" + return 0 + fi + fi + + # 2. Publish the local OpenBao listener on the tailnet (auto-HTTPS). + if tailscale serve status 2>/dev/null | grep -q "${OPENBAO_LISTEN_ADDR}"; then + log_info "Tailscale serve already publishes http://${OPENBAO_LISTEN_ADDR}." + else + log_info "Publishing OpenBao on the tailnet via 'tailscale serve' (HTTPS:443)..." + tailscale serve --bg --https=443 "http://${OPENBAO_LISTEN_ADDR}" \ + || log_warn "tailscale serve failed — enable HTTPS on your tailnet and retry." + fi + + local fqdn + fqdn=$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // ""' | sed 's/\.$//') + if [[ -n "$fqdn" ]]; then + log_info "OpenBao should now be reachable at: https://${fqdn}" + if [[ "$OPENBAO_API_ADDR" != "https://${fqdn}" ]]; then + log_warn "OPENBAO_API_ADDR is '${OPENBAO_API_ADDR}'." + log_warn "For OIDC / UI redirects, set it to 'https://${fqdn}' and re-run, or edit ${BAO_CONFIG_DIR}/config.hcl." + fi + fi +} + # ============================================================ # Proxmox-host helpers # ============================================================ @@ -191,13 +246,22 @@ allocate_ctid() { } # Inject the script into the container and execute it in the requested mode. +# Forwards the relevant runtime configuration through the environment so the +# inner invocation produces the same config the user requested on the host. exec_in_lxc() { local ctid="$1" local mode="$2" # --install or --update # Ensure base tooling exists inside the container before piping the script. pct exec "$ctid" -- sh -c "apk add --no-cache bash curl jq unzip ca-certificates >/dev/null 2>&1" - curl -fsSL "$SCRIPT_URL" | pct exec "$ctid" -- bash -s -- "$mode" + curl -fsSL "$SCRIPT_URL" \ + | pct exec "$ctid" -- env \ + OPENBAO_VERSION="$OPENBAO_VERSION" \ + OPENBAO_HOSTNAME="$HOSTNAME_LXC" \ + OPENBAO_LISTEN_ADDR="$OPENBAO_LISTEN_ADDR" \ + OPENBAO_API_ADDR="$OPENBAO_API_ADDR" \ + TS_AUTHKEY="$TS_AUTHKEY" \ + bash -s -- "$mode" } # ============================================================ @@ -231,6 +295,14 @@ create_lxc() { --onboot 1 \ --start 0 + # Tailscale needs /dev/net/tun inside the unprivileged container. + log_info "Adding /dev/net/tun passthrough for Tailscale..." + cat >> "/etc/pve/lxc/${CTID}.conf" </dev/null + apk add --no-cache bash curl jq unzip ca-certificates gcompat openrc logrotate tailscale >/dev/null + + log_info "Enabling tailscaled..." + rc-update add tailscale default >/dev/null 2>&1 || true + rc-service tailscale start >/dev/null 2>&1 || log_warn "tailscaled failed to start (is /dev/net/tun mapped into the LXC?)" install_or_upgrade_bao @@ -318,6 +394,9 @@ install_inside_lxc() { if [[ ! -f "${BAO_CONFIG_DIR}/config.hcl" ]]; then log_info "Writing default ${BAO_CONFIG_DIR}/config.hcl..." + # OpenBao listens on loopback only; Tailscale (running in the same LXC) + # acts as the reverse proxy and terminates TLS via tailnet certificates. + # https://openbao.org/docs/configuration/ cat > "${BAO_CONFIG_DIR}/config.hcl" </dev/null apk upgrade >/dev/null install_or_upgrade_bao + configure_tailscale_proxy log_info "Update complete." }