From 7899d8fd933dad95a8e047667bfbddcfddb76193 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:16:38 +0200 Subject: [PATCH 01/14] Add OpenBao LXC installation and update scripts --- openbao/README.md | 89 +++++++++ openbao/install.sh | 452 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 541 insertions(+) create mode 100644 openbao/README.md create mode 100755 openbao/install.sh diff --git a/openbao/README.md b/openbao/README.md new file mode 100644 index 0000000..f1aefa6 --- /dev/null +++ b/openbao/README.md @@ -0,0 +1,89 @@ +# OpenBao + +Automated installation and update script for an [OpenBao](https://openbao.org) secrets-manager +server running inside an Alpine LXC on Proxmox. + +### Features + +Single script, automatic mode selection: + +| Context | Action | +| ---------------------------------------------------- | --------------------------------------------------------------------------------- | +| From Proxmox host, no existing OpenBao container | Detects newest Alpine template, creates LXC, installs `bao` + OpenRC service | +| From Proxmox host, OpenBao container already present | Reuses the existing LXC, refreshes packages, upgrades `bao` to the latest release | +| From inside an LXC, no `bao` binary | Installs OpenBao from scratch | +| From inside an LXC, `bao` already present | Updates the binary only (no config / data changes) | + +The container is identified by hostname **and** the `openbao` tag, so it is +re-found across reruns even if the CTID was auto-allocated the first time. + +### Requirements + +- Proxmox VE host with `pveam`, `pct`, `pvesh`, `jq` available +- Internet access from both the host (template download) and the LXC (binary download) +- Script must be run as **root** on the Proxmox host (enforced; the Web UI shell qualifies) + +### Usage + +#### Full install (from Proxmox shell) + +```bash +bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh)" +``` + +Re-running the exact same command later upgrades packages inside the LXC and +brings the `bao` binary to the latest release, without touching the config or +the raft data directory. + +#### Customisation + +Every parameter is exposed as an environment variable: + +| Variable | Default | Description | +| ------------------ | ------------- | ------------------------------------------------------------- | +| `CTID` | auto | Container ID (auto-allocated via `pvesh get /cluster/nextid`) | +| `OPENBAO_HOSTNAME` | `openbao` | LXC hostname (also used as raft `node_id`) | +| `TEMPLATE` | auto-detected | Alpine template; auto-detected from `pveam available` | +| `STORAGE` | `local-lvm` | Proxmox storage for the LXC root disk | +| `TEMPLATE_STORAGE` | `local` | Storage where Alpine templates live | +| `CORES` | `2` | vCPU cores | +| `RAM` | `1024` | RAM in MiB | +| `DISK` | `8` | Root disk size in GB | +| `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` | + +```bash +CTID=210 OPENBAO_HOSTNAME=vault CORES=4 RAM=2048 \ + bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh)" +``` + +#### First-time initialisation + +OpenBao starts sealed. Once the LXC is up: + +```bash +pct enter +export VAULT_ADDR=http://127.0.0.1:8200 +bao operator init # save the unseal keys + root token somewhere safe +bao operator unseal # repeat with each key share until unsealed +``` + +#### Update (from inside the LXC) + +```bash +curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh | bash +``` + +The script auto-detects the presence of `/usr/local/bin/bao` and switches to +update mode. The OpenRC service is stopped, the binary is swapped (the old one +is kept as `bao.bak.`), then the service is restarted. + +### Architecture + +- **OS**: latest Alpine LXC template (auto-detected), unprivileged, `nesting=1` +- **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) +- **Version tracking**: `/opt/openbao_version.txt` records the currently installed tag for idempotent reruns diff --git a/openbao/install.sh b/openbao/install.sh new file mode 100755 index 0000000..2350c03 --- /dev/null +++ b/openbao/install.sh @@ -0,0 +1,452 @@ +#!/bin/bash +# install.sh - OpenBao: LXC creation, installation & update +# Usage: +# From Proxmox host : bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh)" +# From inside LXC : bash /root/install.sh (updates bao binary) +# +# Single entrypoint, three automatic modes: +# 1. Proxmox host, no existing container -> create LXC + install OpenBao +# 2. Proxmox host, container already present -> update packages + upgrade bao +# 3. Inside an LXC -> install bao if missing, otherwise update +# +# The OpenBao binary install/upgrade logic lives in a single reusable function +# (install_or_upgrade_bao) shared by both the create and update paths. + +set -euo pipefail + +# --- Config (override via environment) --- +CTID="${CTID:-}" +HOSTNAME_LXC="${OPENBAO_HOSTNAME:-openbao}" +TEMPLATE="${TEMPLATE:-}" # auto-detected when empty +STORAGE="${STORAGE:-local-lvm}" +TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}" +CORES="${CORES:-2}" +RAM="${RAM:-1024}" +DISK="${DISK:-8}" +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" +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" +BAO_CONFIG_DIR="/etc/openbao" +BAO_DATA_DIR="/var/lib/openbao" + +# --- Colors --- +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# ============================================================ +# Generic helpers +# ============================================================ +require_root() { + if [[ "$(id -u)" -ne 0 ]]; then + log_error "This script must be run as root (current uid: $(id -u))." + log_error "On Proxmox, launch it from the host shell or via the Web UI shell, both of which run as root." + exit 1 + fi +} + +get_arch() { + case "$(uname -m)" in + x86_64) echo "amd64" ;; + aarch64) echo "arm64" ;; + *) log_error "Unsupported architecture: $(uname -m)"; exit 1 ;; + esac +} + +# Resolve "latest" -> concrete tag name, otherwise echo input unchanged. +resolve_openbao_version() { + local requested="$1" + if [[ "$requested" != "latest" ]]; then + echo "$requested" + return 0 + fi + local tag + tag=$(curl -fsSL "${OPENBAO_API}/latest" | jq -r '.tag_name') + if [[ -z "$tag" || "$tag" == "null" ]]; then + log_error "Failed to resolve latest OpenBao release from GitHub API." + exit 1 + fi + echo "$tag" +} + +# ============================================================ +# Reusable: install or upgrade the bao binary in-place. +# Used by both fresh-install and update flows. +# Returns 0 on success, exits on hard error. +# ============================================================ +install_or_upgrade_bao() { + local tag arch version url tmpdir current + tag=$(resolve_openbao_version "$OPENBAO_VERSION") + arch=$(get_arch) + version="${tag#v}" + + current="" + if [[ -f "$VERSION_FILE" ]]; then + current=$(cat "$VERSION_FILE") + fi + + if [[ "$current" == "$tag" && -x /usr/local/bin/bao ]]; then + log_info "OpenBao already at $tag, nothing to do." + return 0 + fi + + # OpenBao publishes zip archives named bao__linux_.zip + url="https://github.com/openbao/openbao/releases/download/${tag}/bao_${version}_linux_${arch}.zip" + log_info "Downloading OpenBao ${tag} (${arch}) from ${url}..." + + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' RETURN + curl -fsSL "$url" -o "${tmpdir}/bao.zip" + unzip -q -o "${tmpdir}/bao.zip" -d "$tmpdir" + + if [[ ! -f "${tmpdir}/bao" ]]; then + log_error "Archive did not contain expected 'bao' binary." + exit 1 + fi + + # Stop service if running, swap binary atomically, then restart. + local service_was_running=0 + if command -v rc-service >/dev/null 2>&1 && rc-service openbao status >/dev/null 2>&1; then + service_was_running=1 + log_info "Stopping openbao service for upgrade..." + rc-service openbao stop || true + fi + + if [[ -x /usr/local/bin/bao ]]; then + cp /usr/local/bin/bao "/usr/local/bin/bao.bak.$(date +%s)" + fi + install -m 0755 "${tmpdir}/bao" /usr/local/bin/bao + ln -sf /usr/local/bin/bao /usr/bin/bao + echo "$tag" > "$VERSION_FILE" + + log_info "Installed: $(/usr/local/bin/bao --version 2>&1 | head -n1 || true)" + + if [[ "$service_was_running" -eq 1 ]]; then + log_info "Restarting openbao service..." + rc-service openbao start + fi +} + +# ============================================================ +# Proxmox-host helpers +# ============================================================ + +# Detect newest Alpine LXC template available from the Proxmox repos. +detect_latest_alpine_template() { + local tmpl + tmpl=$(pveam available --section system 2>/dev/null \ + | awk '/^system[[:space:]]+alpine-/ {print $2}' \ + | sort -V \ + | tail -n1) + + if [[ -z "$tmpl" ]]; then + log_warn "Could not query pveam; falling back to a known-good Alpine template." + tmpl="alpine-3.22-default_20250617_amd64.tar.xz" + fi + log_info "Selected Alpine template: $tmpl" + echo "$tmpl" +} + +# Find an existing LXC by tag or hostname. Echoes CTID, returns 1 if none. +find_existing_lxc() { + local id host tags + while read -r id _; do + [[ -z "$id" || "$id" == "VMID" ]] && continue + host=$(pct config "$id" 2>/dev/null | awk -F': ' '/^hostname:/ {print $2}' || true) + tags=$(pct config "$id" 2>/dev/null | awk -F': ' '/^tags:/ {print $2}' || true) + if [[ "$host" == "$HOSTNAME_LXC" ]] || [[ ",${tags//;/,}," == *",${LXC_TAG},"* ]]; then + echo "$id" + return 0 + fi + done < <(pct list | awk 'NR>1 {print $1}') + return 1 +} + +ensure_template_present() { + local tmpl="$1" + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$tmpl"; then + log_info "Downloading template ${tmpl} to storage ${TEMPLATE_STORAGE}..." + pveam update >/dev/null + pveam download "$TEMPLATE_STORAGE" "$tmpl" + else + log_info "Template ${tmpl} already present on ${TEMPLATE_STORAGE}." + fi +} + +# Pick next available CTID if user did not provide one. +allocate_ctid() { + pvesh get /cluster/nextid 2>/dev/null \ + || pvesh get /cluster/resources --type vm --output-format json 2>/dev/null \ + | jq '[.[].vmid] | max + 1' \ + || echo 100 +} + +# Inject the script into the container and execute it in the requested mode. +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" +} + +# ============================================================ +# MODE: Proxmox host — create LXC + install +# ============================================================ +create_lxc() { + log_info "=== OpenBao — LXC creation ===" + + if [[ -z "$TEMPLATE" ]]; then + TEMPLATE=$(detect_latest_alpine_template) + else + log_info "Using user-provided template: $TEMPLATE" + fi + ensure_template_present "$TEMPLATE" + + if [[ -z "$CTID" ]]; then + CTID=$(allocate_ctid) + log_info "Auto-selected CTID: $CTID" + fi + + log_info "Creating LXC ${CTID} (${HOSTNAME_LXC})..." + pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" \ + --hostname "$HOSTNAME_LXC" \ + --cores "$CORES" \ + --memory "$RAM" \ + --rootfs "${STORAGE}:${DISK}" \ + --net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" \ + --unprivileged 1 \ + --features "nesting=1" \ + --tags "infra-script,${LXC_TAG}" \ + --onboot 1 \ + --start 0 + + log_info "Starting LXC ${CTID}..." + pct start "$CTID" + # Wait for network to come up + local tries=0 + until pct exec "$CTID" -- sh -c "ip -4 addr show eth0 | grep -q 'inet '" 2>/dev/null; do + tries=$((tries + 1)) + if (( tries > 20 )); then + log_error "LXC ${CTID} did not acquire an IP after 20s." + exit 1 + fi + sleep 1 + done + + log_info "Running installer inside LXC ${CTID}..." + exec_in_lxc "$CTID" "--install" + + local ip + ip=$(pct exec "$CTID" -- ip -4 addr show eth0 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1 || true) + + echo "" + log_info "=========================================" + log_info "LXC ${CTID} created successfully!" + log_info "=========================================" + echo "" + echo " Hostname : ${HOSTNAME_LXC}" + echo " IP : ${ip:-pending}" + echo " API : http://${ip:-}:8200" + echo "" + echo "Next steps:" + echo " pct enter ${CTID}" + echo " export VAULT_ADDR=http://127.0.0.1:8200" + echo " bao operator init # initialise & retrieve unseal keys + root token" + echo " bao operator unseal # repeat with the unseal keys" + echo "" +} + +# ============================================================ +# MODE: Proxmox host — update existing LXC +# ============================================================ +update_lxc() { + local ctid="$1" + log_info "=== OpenBao — updating existing LXC ${ctid} ===" + + if ! pct status "$ctid" | grep -q running; then + log_info "Starting LXC ${ctid}..." + pct start "$ctid" + sleep 3 + fi + + log_info "Refreshing Alpine packages inside LXC ${ctid}..." + pct exec "$ctid" -- sh -c "apk update >/dev/null && apk upgrade >/dev/null" + + log_info "Upgrading bao binary inside LXC ${ctid}..." + exec_in_lxc "$ctid" "--update" + + log_info "Update of LXC ${ctid} complete." +} + +# ============================================================ +# MODE: inside LXC — fresh install of OpenBao +# ============================================================ +install_inside_lxc() { + log_info "=== OpenBao — installation ===" + + log_info "Updating package index..." + apk update >/dev/null + apk upgrade >/dev/null + + log_info "Installing dependencies..." + # gcompat: OpenBao binaries are glibc-built; gcompat is required on musl Alpine. + apk add --no-cache bash curl jq unzip ca-certificates gcompat openrc logrotate >/dev/null + + install_or_upgrade_bao + + log_info "Creating ${BAO_USER} system user..." + if ! id "$BAO_USER" >/dev/null 2>&1; then + addgroup -S "$BAO_USER" 2>/dev/null || true + adduser -S -D -H -h "$BAO_DATA_DIR" -s /sbin/nologin -G "$BAO_USER" "$BAO_USER" + fi + + log_info "Provisioning directories..." + mkdir -p "$BAO_CONFIG_DIR" "$BAO_DATA_DIR/data" + chown -R "${BAO_USER}:${BAO_USER}" "$BAO_DATA_DIR" + chmod 750 "$BAO_DATA_DIR" + + if [[ ! -f "${BAO_CONFIG_DIR}/config.hcl" ]]; then + log_info "Writing default ${BAO_CONFIG_DIR}/config.hcl..." + cat > "${BAO_CONFIG_DIR}/config.hcl" < /etc/init.d/openbao <<'EOF' +#!/sbin/openrc-run + +name="OpenBao" +description="OpenBao secrets manager" +command="/usr/local/bin/bao" +command_args="server -config=/etc/openbao/config.hcl" +command_user="openbao:openbao" +command_background=true +pidfile="/run/${RC_SVCNAME}.pid" +directory="/var/lib/openbao" + +output_log="/var/log/openbao.log" +error_log="/var/log/openbao.log" + +depend() { + need net + after net +} + +start_pre() { + checkpath --directory --owner openbao:openbao --mode 0750 /var/lib/openbao + checkpath --directory --owner openbao:openbao --mode 0750 /var/lib/openbao/data + checkpath --file --owner openbao:openbao --mode 0644 /var/log/openbao.log +} +EOF + chmod +x /etc/init.d/openbao + rc-update add openbao default >/dev/null + + cat > /etc/logrotate.d/openbao <<'EOF' +/var/log/openbao.log { + daily + rotate 7 + compress + missingok + notifempty + copytruncate +} +EOF + ln -sf /usr/sbin/logrotate /etc/periodic/daily/logrotate 2>/dev/null || true + + log_info "Starting openbao service..." + rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log" + + log_info "Cleaning up..." + rm -rf /var/cache/apk/* + + echo "" + log_info "=========================================" + log_info "OpenBao installation complete!" + log_info "=========================================" + echo "" + echo "Initialise the server with:" + echo " export VAULT_ADDR=http://127.0.0.1:8200" + echo " bao operator init" + echo " bao operator unseal # repeat with each unseal key share" + echo "" +} + +# ============================================================ +# MODE: inside LXC — update only +# ============================================================ +update_inside_lxc() { + log_info "=== OpenBao — update ===" + apk update >/dev/null + apk upgrade >/dev/null + install_or_upgrade_bao + log_info "Update complete." +} + +# ============================================================ +# Main — dispatch on explicit mode flag or auto-detect context +# ============================================================ +main() { + case "${1:-}" in + --install) + install_inside_lxc + return + ;; + --update) + update_inside_lxc + return + ;; + esac + + if command -v pct >/dev/null 2>&1; then + # Running on a Proxmox host + require_root + + local existing="" + if existing=$(find_existing_lxc); then + log_info "Found existing OpenBao LXC (CTID ${existing}, hostname/tag match) — switching to update mode." + update_lxc "$existing" + else + create_lxc + fi + else + # Inside a container (no Proxmox tooling) + require_root + if [[ -x /usr/local/bin/bao ]]; then + update_inside_lxc + else + install_inside_lxc + fi + fi +} + +main "$@" -- 2.54.0 From 530e7dce2ac548ee748a8c1e0ffc11ecca64b5b2 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:25:39 +0200 Subject: [PATCH 02/14] 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." } -- 2.54.0 From 636a637e813e2009fbe820743d0d4513e55cea6c Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:26:02 +0200 Subject: [PATCH 03/14] Add key features to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 73a4580..5ff2da3 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ These scripts automate the deployment of personal infrastructure components. The - **Idempotent-ish**: Safe to re-run (where possible) - **Curl-friendly**: Designed for one-liner deployment from a fresh server - **Multi-OS**: Supports Debian and Alpine-based deployments +- **Loopback by default**: Services bind to `127.0.0.1`; Tailscale handles the reverse proxy and TLS termination +- **Log hygiene**: Every long-running service ships with a `logrotate` config (no unbounded log files) +- **Keep it simple**: One script per service, plain bash, no frameworks — readability over cleverness ### Available Scripts -- 2.54.0 From ec77eda69e2710e8c1859649663a59bb48f1c11e Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:26:37 +0200 Subject: [PATCH 04/14] Format README table for better readability --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5ff2da3..28852b1 100644 --- a/README.md +++ b/README.md @@ -16,15 +16,15 @@ These scripts automate the deployment of personal infrastructure components. The ### Available Scripts -| Script | Description | Usage | -|--------|-------------|-------| -| [`proxy/install.sh`](proxy/) | Reverse proxy with Tailscale + Nginx Proxy Manager | `curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/proxy/install.sh` \| `bash` | -| [`netlab/install.sh`](netlab/) | Network lab with ContainerLab | `curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/netlab/install.sh` \| `bash` | -| [`seedbox/install.sh`](seedbox/) | ISO seedbox with Transmission + NFS | `NFS_SERVER= curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh` \| `bash` | -| [`gitea-runner/install.sh`](gitea-runner/) | Gitea Act Runner on Alpine LXC (Proxmox) | `bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh)"` | +| Script | Description | Usage | +| ------------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| [`proxy/install.sh`](proxy/) | Reverse proxy with Tailscale + Nginx Proxy Manager | `curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/proxy/install.sh` \| `bash` | +| [`netlab/install.sh`](netlab/) | Network lab with ContainerLab | `curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/netlab/install.sh` \| `bash` | +| [`seedbox/install.sh`](seedbox/) | ISO seedbox with Transmission + NFS | `NFS_SERVER= curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh` \| `bash` | +| [`gitea-runner/install.sh`](gitea-runner/) | Gitea Act Runner on Alpine LXC (Proxmox) | `bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/gitea-runner/install.sh)"` | ### Requirements - Fresh Debian 12/13 installation (proxy, netlab, seedbox) or Proxmox VE host (gitea-runner) - User with sudo privileges (do not run as root) — except gitea-runner which runs as root on Proxmox -- Internet access \ No newline at end of file +- Internet access -- 2.54.0 From e8920b246b40cb36c6d58386669717ca10e1ab94 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:28:07 +0200 Subject: [PATCH 05/14] Enable console auto-login on Proxmox LXCs Enable root auto-login on tty1 for faster shell access in Proxmox LXCs by configuring agetty with autologin and adding the necessary inittab entry --- README.md | 1 + openbao/install.sh | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 28852b1..836f2a9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ These scripts automate the deployment of personal infrastructure components. The - **Multi-OS**: Supports Debian and Alpine-based deployments - **Loopback by default**: Services bind to `127.0.0.1`; Tailscale handles the reverse proxy and TLS termination - **Log hygiene**: Every long-running service ships with a `logrotate` config (no unbounded log files) +- **Console auto-login**: Proxmox LXCs are configured for root auto-login on `tty1` (fast `pct enter` and Web UI shell access) - **Keep it simple**: One script per service, plain bash, no frameworks — readability over cleverness ### Available Scripts diff --git a/openbao/install.sh b/openbao/install.sh index 069c749..883129d 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -465,6 +465,13 @@ EOF log_info "Starting openbao service..." rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log" + log_info "Enabling console auto-login on tty1..." + mkdir -p /etc/conf.d + cat > /etc/conf.d/agetty <<'EOF' +agetty_options="--autologin root --noclear" +EOF + sed -i 's|^tty1::respawn:/sbin/getty.*|tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1|' /etc/inittab + configure_tailscale_proxy log_info "Cleaning up..." -- 2.54.0 From 03d7b13f89736391190edb7cfd78a0c4c9a7552e Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 15:36:14 +0200 Subject: [PATCH 06/14] Update install script URL to main branch --- openbao/README.md | 2 +- openbao/install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openbao/README.md b/openbao/README.md index 6166b33..7740a27 100644 --- a/openbao/README.md +++ b/openbao/README.md @@ -58,7 +58,7 @@ Every parameter is exposed as an environment variable: ```bash CTID=210 OPENBAO_HOSTNAME=vault CORES=4 RAM=2048 \ - bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/feat/lxc-OpenBao/openbao/install.sh)" + bash -c "$(curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh)" ``` #### First-time initialisation diff --git a/openbao/install.sh b/openbao/install.sh index 883129d..a272793 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -35,7 +35,7 @@ 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" +SCRIPT_URL="https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh" VERSION_FILE="/opt/openbao_version.txt" BAO_USER="openbao" BAO_CONFIG_DIR="/etc/openbao" -- 2.54.0 From 274e728aeb6dce044730ff3655e65c20c9307164 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:02:37 +0200 Subject: [PATCH 07/14] Update OpenBao README and add MOTD configuration ``` Update environment variable table formatting for readability Fix data directory path formatting in README Add MOTD with OpenBao status and access information ``` --- openbao/README.md | 34 ++++++++++++++++---------------- openbao/install.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/openbao/README.md b/openbao/README.md index 7740a27..184c629 100644 --- a/openbao/README.md +++ b/openbao/README.md @@ -39,22 +39,22 @@ the raft data directory. Every parameter is exposed as an environment variable: -| Variable | Default | Description | -| ------------------ | ------------- | ------------------------------------------------------------- | -| `CTID` | auto | Container ID (auto-allocated via `pvesh get /cluster/nextid`) | -| `OPENBAO_HOSTNAME` | `openbao` | LXC hostname (also used as raft `node_id`) | -| `TEMPLATE` | auto-detected | Alpine template; auto-detected from `pveam available` | -| `STORAGE` | `local-lvm` | Proxmox storage for the LXC root disk | -| `TEMPLATE_STORAGE` | `local` | Storage where Alpine templates live | -| `CORES` | `2` | vCPU cores | -| `RAM` | `1024` | RAM in MiB | -| `DISK` | `8` | Root disk size in GB | -| `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. | +| Variable | Default | Description | +| --------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `CTID` | auto | Container ID (auto-allocated via `pvesh get /cluster/nextid`) | +| `OPENBAO_HOSTNAME` | `openbao` | LXC hostname (also used as raft `node_id`) | +| `TEMPLATE` | auto-detected | Alpine template; auto-detected from `pveam available` | +| `STORAGE` | `local-lvm` | Proxmox storage for the LXC root disk | +| `TEMPLATE_STORAGE` | `local` | Storage where Alpine templates live | +| `CORES` | `2` | vCPU cores | +| `RAM` | `1024` | RAM in MiB | +| `DISK` | `8` | Root disk size in GB | +| `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 \ @@ -119,5 +119,5 @@ is kept as `bao.bak.`), then the service is restarted. - **Service**: OpenRC, runs as user `openbao`, logs to `/var/log/openbao.log` (rotated daily, 7 days retained) - **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) +- **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 a272793..2a8557c 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -474,6 +474,54 @@ EOF configure_tailscale_proxy + log_info "Configuring MOTD..." + # /etc/profile.d/ runs for every interactive login shell — works for both + # the auto-login tty and Tailscale SSH. Quoted heredoc: every variable is + # resolved at login time, not at install time. + cat > /etc/profile.d/00-openbao.sh <<'MOTD' +TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' ' + /"Self"/ { in_self=1 } + in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit } +') +[[ -z "$TS_FQDN" ]] && TS_FQDN="$(hostname).ts.net" + +BAO_VERSION=$(cat /opt/openbao_version.txt 2>/dev/null || echo "unknown") + +# `bao status` exit codes: 0 = unsealed, 2 = sealed, anything else = error. +VAULT_ADDR=http://127.0.0.1:8200 /usr/local/bin/bao status >/dev/null 2>&1 +case $? in + 0) SEAL_STATE="unsealed" ;; + 2) SEAL_STATE="SEALED (run: bao operator unseal)" ;; + *) SEAL_STATE="unreachable" ;; +esac + +echo "" +echo " ___ ____ " +echo " / _ \ _ __ ___ _ __ | __ ) __ _ ___ " +echo "| | | | '_ \ / _ \ '_ \| _ \ / _\` |/ _ \\" +echo "| |_| | |_) | __/ | | | |_) | (_| | (_) |" +echo " \___/| .__/ \___|_| |_|____/ \__,_|\___/" +echo " |_| " +echo "" +echo "OpenBao Secrets Manager (${BAO_VERSION})" +echo "─────────────────────────────────────────" +echo "Access:" +echo " • API (local) : http://127.0.0.1:8200" +echo " • Tailnet : https://${TS_FQDN}" +echo " • Seal status : ${SEAL_STATE}" +echo "" +echo "Useful commands:" +echo " export VAULT_ADDR=http://127.0.0.1:8200" +echo " bao status" +echo " bao operator init # first-time only" +echo " bao operator unseal # after every restart" +echo " rc-service openbao status" +echo " tail -f /var/log/openbao.log" +echo "─────────────────────────────────────────" +echo "" +MOTD + chmod +x /etc/profile.d/00-openbao.sh + log_info "Cleaning up..." rm -rf /var/cache/apk/* -- 2.54.0 From c52a5b7fea4733ed72fa99e23d9483e3ce7fe924 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:04:35 +0200 Subject: [PATCH 08/14] Redirect logging functions to stderr --- openbao/install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openbao/install.sh b/openbao/install.sh index 2a8557c..e1cf69d 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -47,9 +47,10 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' -log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } -log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -log_error() { echo -e "${RED}[ERROR]${NC} $1"; } +# Logs go to stderr so callers can safely use $(fn) without capturing log noise. +log_info() { echo -e "${GREEN}[INFO]${NC} $1" >&2; } +log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2; } +log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; } # ============================================================ # Generic helpers -- 2.54.0 From 10f9a167825b8cbe9a373977646c2d826ed34780 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:20:47 +0200 Subject: [PATCH 09/14] Allow overriding SCRIPT_URL and VERSION_FILE --- openbao/install.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openbao/install.sh b/openbao/install.sh index e1cf69d..30688c6 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -35,8 +35,11 @@ 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/main/openbao/install.sh" -VERSION_FILE="/opt/openbao_version.txt" +# SCRIPT_URL is what the host-side flow pipes into the LXC. Override it when +# testing from a non-main branch, e.g. +# SCRIPT_URL="https://gitea.arnodo.fr/.../branch/feat/lxc-OpenBao/openbao/install.sh" +SCRIPT_URL="${SCRIPT_URL:-https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/openbao/install.sh}" +VERSION_FILE="${VERSION_FILE:-/opt/openbao_version.txt}" BAO_USER="openbao" BAO_CONFIG_DIR="/etc/openbao" BAO_DATA_DIR="/var/lib/openbao" @@ -257,6 +260,7 @@ exec_in_lxc() { 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" -- env \ + SCRIPT_URL="$SCRIPT_URL" \ OPENBAO_VERSION="$OPENBAO_VERSION" \ OPENBAO_HOSTNAME="$HOSTNAME_LXC" \ OPENBAO_LISTEN_ADDR="$OPENBAO_LISTEN_ADDR" \ -- 2.54.0 From 34e0ce087dbb6ae0666f1db99acb7b76f17151df Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:25:25 +0200 Subject: [PATCH 10/14] Update install script to use configurable GitHub releases URL --- openbao/install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openbao/install.sh b/openbao/install.sh index 30688c6..deadec5 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -26,7 +26,8 @@ DISK="${DISK:-8}" 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" +# GitHub releases endpoint for the openbao/openbao repo (used to resolve "latest"). +OPENBAO_RELEASES_URL="${OPENBAO_RELEASES_URL:-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, @@ -82,7 +83,7 @@ resolve_openbao_version() { return 0 fi local tag - tag=$(curl -fsSL "${OPENBAO_API}/latest" | jq -r '.tag_name') + tag=$(curl -fsSL "${OPENBAO_RELEASES_URL}/latest" | jq -r '.tag_name') if [[ -z "$tag" || "$tag" == "null" ]]; then log_error "Failed to resolve latest OpenBao release from GitHub API." exit 1 -- 2.54.0 From c7f44aa29368ec0a85bd303836249a74aa557b27 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:38:38 +0200 Subject: [PATCH 11/14] Update install script for OpenBao release asset changes OpenBao now uses raw architecture names (x86_64, aarch64) instead of Go-style (amd64, arm64) and publishes .tar.gz archives instead of .zip files. This commit updates the install script to match the new naming convention and archive format. --- openbao/install.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/openbao/install.sh b/openbao/install.sh index deadec5..c5cc2c3 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -67,10 +67,12 @@ require_root() { fi } +# OpenBao publishes release assets named with the raw `uname -m` arch +# (e.g. x86_64, aarch64), not Go-style amd64/arm64. get_arch() { case "$(uname -m)" in - x86_64) echo "amd64" ;; - aarch64) echo "arm64" ;; + x86_64) echo "x86_64" ;; + aarch64) echo "aarch64" ;; *) log_error "Unsupported architecture: $(uname -m)"; exit 1 ;; esac } @@ -112,14 +114,14 @@ install_or_upgrade_bao() { return 0 fi - # OpenBao publishes zip archives named bao__linux_.zip - url="https://github.com/openbao/openbao/releases/download/${tag}/bao_${version}_linux_${arch}.zip" + # Asset naming convention: bao__Linux_.tar.gz + url="https://github.com/openbao/openbao/releases/download/${tag}/bao_${version}_Linux_${arch}.tar.gz" log_info "Downloading OpenBao ${tag} (${arch}) from ${url}..." tmpdir=$(mktemp -d) trap 'rm -rf "$tmpdir"' RETURN - curl -fsSL "$url" -o "${tmpdir}/bao.zip" - unzip -q -o "${tmpdir}/bao.zip" -d "$tmpdir" + curl -fsSL "$url" -o "${tmpdir}/bao.tar.gz" + tar -xzf "${tmpdir}/bao.tar.gz" -C "$tmpdir" if [[ ! -f "${tmpdir}/bao" ]]; then log_error "Archive did not contain expected 'bao' binary." @@ -258,7 +260,7 @@ exec_in_lxc() { 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" + pct exec "$ctid" -- sh -c "apk add --no-cache bash curl jq ca-certificates >/dev/null 2>&1" curl -fsSL "$SCRIPT_URL" \ | pct exec "$ctid" -- env \ SCRIPT_URL="$SCRIPT_URL" \ @@ -378,8 +380,7 @@ install_inside_lxc() { apk upgrade >/dev/null log_info "Installing dependencies..." - # gcompat: OpenBao binaries are glibc-built; gcompat is required on musl Alpine. - apk add --no-cache bash curl jq unzip ca-certificates gcompat openrc logrotate tailscale >/dev/null + apk add --no-cache bash curl jq ca-certificates gcompat openrc logrotate tailscale >/dev/null log_info "Enabling tailscaled..." rc-update add tailscale default >/dev/null 2>&1 || true -- 2.54.0 From a6a3b421c524bedf167d7d4cf94a49ca785a4131 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:42:24 +0200 Subject: [PATCH 12/14] Add cleanup for temporary directory Previously the temporary directory was removed via the RETURN trap but this could be bypassed if the script exited early. Now we explicitly clean up the directory in all exit paths. --- openbao/install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openbao/install.sh b/openbao/install.sh index c5cc2c3..af92848 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -119,12 +119,12 @@ install_or_upgrade_bao() { log_info "Downloading OpenBao ${tag} (${arch}) from ${url}..." tmpdir=$(mktemp -d) - trap 'rm -rf "$tmpdir"' RETURN curl -fsSL "$url" -o "${tmpdir}/bao.tar.gz" tar -xzf "${tmpdir}/bao.tar.gz" -C "$tmpdir" if [[ ! -f "${tmpdir}/bao" ]]; then log_error "Archive did not contain expected 'bao' binary." + rm -rf "$tmpdir" exit 1 fi @@ -149,6 +149,8 @@ install_or_upgrade_bao() { log_info "Restarting openbao service..." rc-service openbao start fi + + rm -rf "$tmpdir" } # ============================================================ -- 2.54.0 From bc1ad492102cabf1a8adba8bc215b5760e4cde3a Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:48:34 +0200 Subject: [PATCH 13/14] Install script adds agetty for console autologin --- openbao/install.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openbao/install.sh b/openbao/install.sh index af92848..03be345 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -475,11 +475,18 @@ EOF rc-service openbao start || log_warn "openbao failed to start — inspect /var/log/openbao.log" log_info "Enabling console auto-login on tty1..." - mkdir -p /etc/conf.d - cat > /etc/conf.d/agetty <<'EOF' -agetty_options="--autologin root --noclear" -EOF - sed -i 's|^tty1::respawn:/sbin/getty.*|tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1|' /etc/inittab + # Alpine ships busybox getty by default; agetty (from util-linux) is what + # supports --autologin. + apk add --no-cache agetty >/dev/null 2>&1 || apk add --no-cache util-linux >/dev/null + + # Replace any existing tty1 entry, then append our autologin line. Doing it + # in two steps (delete + append) is more robust than an in-place sed against + # a pattern that may drift across Alpine releases. + sed -i '/^tty1::/d' /etc/inittab + echo 'tty1::respawn:/sbin/agetty --autologin root --noclear 38400 tty1' >> /etc/inittab + + # Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot. + kill -HUP 1 2>/dev/null || true configure_tailscale_proxy -- 2.54.0 From a03af831261e7f60002c0b05e940398928c5fe61 Mon Sep 17 00:00:00 2001 From: Damien Date: Tue, 26 May 2026 17:52:54 +0200 Subject: [PATCH 14/14] Force tty1 respawn after autologin change --- openbao/install.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openbao/install.sh b/openbao/install.sh index 03be345..c8be7f3 100755 --- a/openbao/install.sh +++ b/openbao/install.sh @@ -488,6 +488,11 @@ EOF # Tell PID 1 to re-read /etc/inittab so the change takes effect without a reboot. kill -HUP 1 2>/dev/null || true + # Kick any getty/agetty still attached to tty1 so init respawns it *now* with + # the new line — otherwise the first web-console session lands on the stale + # process and the operator has to type `exit` once before autologin kicks in. + pkill -KILL -f '(getty|agetty).*tty1' 2>/dev/null || true + configure_tailscale_proxy log_info "Configuring MOTD..." -- 2.54.0