feat: unified launcher, multi-OS hardening, login alerts & auto-updates
Restructure around a single entry point (automations.sh) with a Gum wizard and a self-extracting bundle for repo-less installs. Add scripts/oslib.sh so the provisioning scripts (setup-host, harden-ssh, harden-jumphost, sshuser) run on Alpine/Debian/Alma; seed root keys from globals/. - ntfy SSH-login alerts (user, source IP, key, region, jump target) via pam_exec - daily auto-updates: AUTO_REBOOT=idle reboots only when no SSH active; opt-in Alpine stable-branch upgrades (ALLOW_RELEASE_UPGRADE) - cloud-init: generic base/jumphost + per-deployment, which harden SSH by default on fresh VMs - pocket-id: optional WebFinger block (BASE_DOMAIN), tag v2.8.0 - headscale: fix oidc.expiry schema for 0.28 so the container starts - Gitea release workflow on tag (TOKEN_GITEA); repo URLs -> Gitea - README/LICENSE/.gitignore/.gitattributes (force LF)
This commit is contained in:
@@ -122,6 +122,8 @@ deployments/<name>/ # one folder per stack
|
||||
is, then run the scripts.
|
||||
- **Stand up a stack**: each deployment ships its own `cloud-init.yml` (e.g.
|
||||
[`deployments/pocket-id/cloud-init.yml`](deployments/pocket-id/cloud-init.yml)).
|
||||
These assume a fresh VM, so they **harden SSH first** (`harden-ssh.sh`,
|
||||
`HARDEN_SSH=1` by default) and then deploy the stack.
|
||||
|
||||
Fill in `REPO_URL` and the values at the top of the `runcmd` block, paste as
|
||||
instance user-data, and the host configures itself on first boot.
|
||||
|
||||
+3
-1
@@ -74,7 +74,9 @@ ask_deployment_vars() {
|
||||
case "$1" in
|
||||
pocket-id)
|
||||
ask POCKETID_DOMAIN "Public hostname (e.g. id.example.com)"
|
||||
ask ACME_EMAIL "Let's Encrypt email" ;;
|
||||
ask ACME_EMAIL "Let's Encrypt email"
|
||||
ask BASE_DOMAIN "WebFinger base domain (blank = none / use webfinger deployment)" optional
|
||||
if [[ -n "${BASE_DOMAIN:-}" ]]; then ask REDIRECT_URL "Redirect target for the base domain"; fi ;;
|
||||
beszel)
|
||||
ask BESZEL_DOMAIN "Public hostname (e.g. monitoring.example.com)"
|
||||
ask ACME_EMAIL "Let's Encrypt email" ;;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#cloud-config
|
||||
#
|
||||
# Beszel (monitoring hub) — unattended deploy on a fresh Alpine host.
|
||||
# Beszel (monitoring hub) — harden SSH, then deploy, on a fresh Alpine host.
|
||||
#
|
||||
# Fill in REPO_URL and the values in the runcmd block, then paste this as the
|
||||
# instance user-data. DNS for BESZEL_DOMAIN must point at this host and ports
|
||||
@@ -16,15 +16,20 @@ runcmd:
|
||||
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
|
||||
|
||||
# Optional: install shared admin SSH keys from globals/ for root before deploy
|
||||
# . scripts/lib.sh && load_globals \
|
||||
# && install -d -m700 /root/.ssh && resolve_ssh_keys >> /root/.ssh/authorized_keys
|
||||
|
||||
# Optional: harden SSH first (prints a fresh root key — capture it!)
|
||||
# SKIP_PROMPTS=1 FORCE=1 bash scripts/harden-ssh.sh
|
||||
# Harden SSH on this fresh VM: PQ KEX, key-only auth, sshguard. Seeds root
|
||||
# from globals/authorized_keys (or SSH_KEYS_URL). NOTE: harden-ssh also
|
||||
# prints a generated root key to the serial console — capture it, or rely
|
||||
# on the seeded keys.
|
||||
if [ "$HARDEN_SSH" = 1 ]; then
|
||||
SSH_PORT="$SSH_PORT" ALLOWED_IP="$ALLOWED_IP" SKIP_PROMPTS=1 FORCE=1 \
|
||||
bash scripts/harden-ssh.sh
|
||||
fi
|
||||
|
||||
BESZEL_DOMAIN=monitoring.example.com \
|
||||
ACME_EMAIL=admin@example.com \
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#cloud-config
|
||||
#
|
||||
# Headscale (Tailscale control server) — unattended deploy on fresh Alpine.
|
||||
# Headscale (Tailscale control server) — harden SSH, then deploy, on fresh Alpine.
|
||||
#
|
||||
# Fill in REPO_URL and the values in the runcmd block, then paste this as the
|
||||
# instance user-data. DNS for HEADSCALE_DOMAIN must point at this host and
|
||||
@@ -16,15 +16,20 @@ runcmd:
|
||||
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
|
||||
|
||||
# Optional: install shared admin SSH keys from globals/ for root before deploy
|
||||
# . scripts/lib.sh && load_globals \
|
||||
# && install -d -m700 /root/.ssh && resolve_ssh_keys >> /root/.ssh/authorized_keys
|
||||
|
||||
# Optional: harden SSH first (prints a fresh root key — capture it!)
|
||||
# SKIP_PROMPTS=1 FORCE=1 bash scripts/harden-ssh.sh
|
||||
# Harden SSH on this fresh VM: PQ KEX, key-only auth, sshguard. Seeds root
|
||||
# from globals/authorized_keys (or SSH_KEYS_URL). NOTE: harden-ssh also
|
||||
# prints a generated root key to the serial console — capture it, or rely
|
||||
# on the seeded keys.
|
||||
if [ "$HARDEN_SSH" = 1 ]; then
|
||||
SSH_PORT="$SSH_PORT" ALLOWED_IP="$ALLOWED_IP" SKIP_PROMPTS=1 FORCE=1 \
|
||||
bash scripts/harden-ssh.sh
|
||||
fi
|
||||
|
||||
HEADSCALE_DOMAIN=hs.example.com \
|
||||
ACME_EMAIL=admin@example.com \
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# headscale config.yaml -- 0.28.0
|
||||
#
|
||||
# Placeholders __VAR__ are substituted at deploy time by deploy.sh from the
|
||||
# values in .env. Don't set them here directly unless you're running
|
||||
# headscale outside of the deploy script.
|
||||
# Placeholder tokens (UPPER_SNAKE wrapped in double underscores) are
|
||||
# substituted at deploy time by deploy.sh from the values in .env. Don't set
|
||||
# them here directly unless you're running headscale outside the deploy script.
|
||||
|
||||
server_url: https://__HEADSCALE_DOMAIN__
|
||||
listen_addr: 0.0.0.0:8080
|
||||
@@ -56,6 +56,7 @@ dns:
|
||||
- 1.0.0.1
|
||||
- 2606:4700:4700::1111
|
||||
- 2606:4700:4700::1001
|
||||
split: {}
|
||||
search_domains: []
|
||||
extra_records: []
|
||||
|
||||
@@ -80,6 +81,8 @@ oidc:
|
||||
# - tailscale-admins
|
||||
# allowed_domains:
|
||||
# - example.com
|
||||
expiry:
|
||||
from_authentication_path: false
|
||||
refresh_period: 180d
|
||||
# How long a node stays authenticated before re-auth is required. This is a
|
||||
# duration scalar (not a map). use_expiry_from_token overrides it with the
|
||||
# OIDC token's own expiry when true.
|
||||
expiry: 180d
|
||||
use_expiry_from_token: false
|
||||
|
||||
@@ -225,8 +225,9 @@ sed -i \
|
||||
-e "s/__OIDC_CLIENT_SECRET__/$(sed_escape "$OIDC_CLIENT_SECRET")/g" \
|
||||
"$STACK_DIR/config.yaml"
|
||||
|
||||
# Final sanity check
|
||||
grep -q '__.*__' "$STACK_DIR/config.yaml" \
|
||||
# Final sanity check (ignore comment lines so documentation tokens don't
|
||||
# trip a false positive).
|
||||
grep -v '^[[:space:]]*#' "$STACK_DIR/config.yaml" | grep -q '__[A-Za-z_]*__' \
|
||||
&& warn "config.yaml still has un-substituted __PLACEHOLDER__ markers!" \
|
||||
|| true
|
||||
|
||||
@@ -312,55 +313,57 @@ EOF
|
||||
# __ARCHIVE_BELOW__ marker is the embedded tar.gz payload (base64).
|
||||
exit 0
|
||||
__ARCHIVE_BELOW__
|
||||
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
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
# ─── Public hostname ────────────────────────────────────────────────────────
|
||||
# Bare hostname (no scheme) of the pocket-id deployment. Used by Caddy for
|
||||
# TLS issuance, by anubis as the cookie domain, and to derive APP_URL.
|
||||
POCKETID_DOMAIN=id.example.com
|
||||
POCKETID_DOMAIN=
|
||||
|
||||
# Email for Let's Encrypt registration / expiry notifications.
|
||||
ACME_EMAIL=admin@example.com
|
||||
ACME_EMAIL=
|
||||
|
||||
# ─── Pocket-ID ──────────────────────────────────────────────────────────────
|
||||
# APP_URL is set automatically from POCKETID_DOMAIN in compose; no need to
|
||||
@@ -37,8 +37,19 @@ PGID=1000
|
||||
# Only needed while the anubis-pid service is enabled in compose.
|
||||
ANUBIS_PID_KEY=
|
||||
|
||||
# ─── WebFinger (optional) ───────────────────────────────────────────────────
|
||||
# Leave BOTH blank unless the base domain has no other web server and you want
|
||||
# THIS Caddy to also serve /.well-known/webfinger (OIDC discovery for Tailscale
|
||||
# et al.) at the apex. When set, deploy.sh appends a second site block.
|
||||
# Otherwise use the dedicated `webfinger` deployment.
|
||||
# BASE_DOMAIN apex to serve webfinger from (e.g. example.com)
|
||||
# REDIRECT_URL where non-webfinger requests to the apex are 301'd
|
||||
# If you set one you must set both.
|
||||
BASE_DOMAIN=
|
||||
REDIRECT_URL=
|
||||
|
||||
# ─── Image tags ─────────────────────────────────────────────────────────────
|
||||
# Pin for reproducible deploys.
|
||||
POCKETID_TAG=v2
|
||||
POCKETID_TAG=v2.8.0
|
||||
CADDY_TAG=2-alpine
|
||||
ANUBIS_TAG=latest
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Caddyfile for pocket-id stack.
|
||||
# Caddyfile for pocket-id stack (base).
|
||||
#
|
||||
# Two site blocks:
|
||||
# $POCKETID_DOMAIN pocket-id (via anubis PoW gate)
|
||||
# $BASE_DOMAIN /.well-known/webfinger for OIDC discovery; everything
|
||||
# else redirects to $REDIRECT_URL
|
||||
#
|
||||
# An optional WebFinger site block (Caddyfile.webfinger) is appended by
|
||||
# deploy.sh when BASE_DOMAIN is set -- for the case where the base domain has
|
||||
# no other web server and you want this Caddy to serve OIDC discovery too.
|
||||
# Otherwise use the dedicated `webfinger` deployment.
|
||||
|
||||
{
|
||||
email {$ACME_EMAIL}
|
||||
@@ -38,44 +40,3 @@
|
||||
format console
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Base domain: WebFinger for OIDC discovery + redirect everything else.
|
||||
# Required for Tailscale's custom-OIDC setup -- the email's domain must
|
||||
# serve /.well-known/webfinger pointing at the issuer.
|
||||
# ----------------------------------------------------------------------------
|
||||
{$BASE_DOMAIN} {
|
||||
encode zstd gzip
|
||||
|
||||
@webfinger path /.well-known/webfinger
|
||||
handle @webfinger {
|
||||
header Content-Type "application/jrd+json"
|
||||
header Cache-Control "public, max-age=3600"
|
||||
templates
|
||||
respond `{
|
||||
"subject": "{{.Req.URL.Query.Get "resource"}}",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://openid.net/specs/connect/1.0/issuer",
|
||||
"href": "https://{$POCKETID_DOMAIN}"
|
||||
}
|
||||
]
|
||||
}` 200
|
||||
}
|
||||
|
||||
handle {
|
||||
redir {$REDIRECT_URL}{uri} permanent
|
||||
}
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options "nosniff"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
-Server
|
||||
}
|
||||
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# OPTIONAL: appended to the deployed Caddyfile by deploy.sh when BASE_DOMAIN is
|
||||
# set. Serves /.well-known/webfinger at the base domain (so Tailscale et al.
|
||||
# can discover the OIDC issuer) and redirects everything else to $REDIRECT_URL.
|
||||
# Needs BASE_DOMAIN + REDIRECT_URL in .env (passed to caddy via compose).
|
||||
# ----------------------------------------------------------------------------
|
||||
{$BASE_DOMAIN} {
|
||||
encode zstd gzip
|
||||
|
||||
@webfinger path /.well-known/webfinger
|
||||
handle @webfinger {
|
||||
header Content-Type "application/jrd+json"
|
||||
header Cache-Control "public, max-age=3600"
|
||||
templates
|
||||
respond `{
|
||||
"subject": "{{.Req.URL.Query.Get "resource"}}",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://openid.net/specs/connect/1.0/issuer",
|
||||
"href": "https://{$POCKETID_DOMAIN}"
|
||||
}
|
||||
]
|
||||
}` 200
|
||||
}
|
||||
|
||||
handle {
|
||||
redir {$REDIRECT_URL}{uri} permanent
|
||||
}
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options "nosniff"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
-Server
|
||||
}
|
||||
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,11 @@ identity provider for the other stacks (headscale, beszel, webfinger).
|
||||
`MAXMIND_LICENSE_KEY` (audit-log geolocation) is optional. See
|
||||
[`.env.example`](.env.example) for the full list.
|
||||
|
||||
**Optional WebFinger:** set `BASE_DOMAIN` (and `REDIRECT_URL`) to have this
|
||||
Caddy also serve `/.well-known/webfinger` at the apex — useful when the base
|
||||
domain has no other web server. Leave both blank to run pocket-id only and use
|
||||
the dedicated [`webfinger`](../webfinger/) deployment instead.
|
||||
|
||||
## Deploy
|
||||
|
||||
From the repo root via the launcher:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build.sh -- (re)embed docker-compose.yml, Caddyfile, .env.example into
|
||||
# deploy.sh as a base64-encoded tar.gz payload after the __ARCHIVE_BELOW__
|
||||
# marker. Idempotent: strips any existing payload first.
|
||||
# build.sh -- (re)embed docker-compose.yml, Caddyfile, Caddyfile.webfinger and
|
||||
# .env.example into deploy.sh as a base64-encoded tar.gz payload after the
|
||||
# __ARCHIVE_BELOW__ marker. Idempotent: strips any existing payload first.
|
||||
#
|
||||
# Run after editing any of the loose files. The resulting deploy.sh is
|
||||
# self-contained and can be scp'd to the target box on its own.
|
||||
@@ -14,13 +14,13 @@ 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 Caddyfile .env.example; do
|
||||
for f in docker-compose.yml Caddyfile Caddyfile.webfinger .env.example; do
|
||||
[[ -f "$DIR/$f" ]] || { echo "Missing $DIR/$f" >&2; exit 1; }
|
||||
done
|
||||
|
||||
# tar -> gzip -> base64. Files only (no leading ./), wrapped at 76 cols
|
||||
# so the embedded blob is git-friendly.
|
||||
PAYLOAD=$(tar -czf - -C "$DIR" docker-compose.yml Caddyfile .env.example | base64)
|
||||
PAYLOAD=$(tar -czf - -C "$DIR" docker-compose.yml Caddyfile Caddyfile.webfinger .env.example | base64)
|
||||
|
||||
# Strip any existing payload (everything from MARKER to EOF), then append a
|
||||
# fresh one. If MARKER isn't present, sed leaves the file unchanged.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#cloud-config
|
||||
#
|
||||
# Pocket-ID (OIDC provider) — unattended deploy on a fresh Alpine host.
|
||||
# Pocket-ID (OIDC provider) — harden SSH, then deploy, on a fresh Alpine host.
|
||||
#
|
||||
# Fill in REPO_URL and the values in the runcmd block, then paste this as the
|
||||
# instance user-data. DNS for POCKETID_DOMAIN must point at this host and
|
||||
@@ -15,15 +15,20 @@ runcmd:
|
||||
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
|
||||
|
||||
# Optional: install shared admin SSH keys from globals/ for root before deploy
|
||||
# . scripts/lib.sh && load_globals \
|
||||
# && install -d -m700 /root/.ssh && resolve_ssh_keys >> /root/.ssh/authorized_keys
|
||||
|
||||
# Optional: harden SSH first (prints a fresh root key — capture it!)
|
||||
# SKIP_PROMPTS=1 FORCE=1 bash scripts/harden-ssh.sh
|
||||
# Harden SSH on this fresh VM: PQ KEX, key-only auth, sshguard. Seeds root
|
||||
# from globals/authorized_keys (or SSH_KEYS_URL). NOTE: harden-ssh also
|
||||
# prints a generated root key to the serial console — capture it, or rely
|
||||
# on the seeded keys.
|
||||
if [ "$HARDEN_SSH" = 1 ]; then
|
||||
SSH_PORT="$SSH_PORT" ALLOWED_IP="$ALLOWED_IP" SKIP_PROMPTS=1 FORCE=1 \
|
||||
bash scripts/harden-ssh.sh
|
||||
fi
|
||||
|
||||
POCKETID_DOMAIN=id.example.com \
|
||||
ACME_EMAIL=admin@example.com \
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
# 5. Enables docker on boot, runs `docker compose pull && up -d`.
|
||||
# 6. Waits for healthchecks to go green.
|
||||
#
|
||||
# Self-contained: docker-compose.yml, Caddyfile, and .env.example are
|
||||
# embedded at the bottom of this file as a base64-encoded tar.gz. The
|
||||
# script extracts them at runtime, so this single file is all you need
|
||||
# on the target box.
|
||||
# Self-contained: docker-compose.yml, Caddyfile, Caddyfile.webfinger and
|
||||
# .env.example are embedded at the bottom of this file as a base64-encoded
|
||||
# tar.gz. The script extracts them at runtime, so this single file is all you
|
||||
# need on the target box. The WebFinger block is appended only when BASE_DOMAIN
|
||||
# is set in .env.
|
||||
#
|
||||
# To rebuild after editing the loose files: run ./build.sh in this dir.
|
||||
#
|
||||
@@ -39,6 +40,8 @@ set -euo pipefail
|
||||
[[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1
|
||||
: "${POCKETID_DOMAIN:=}"
|
||||
: "${ACME_EMAIL:=}"
|
||||
: "${BASE_DOMAIN:=}" # optional: enables the WebFinger block (set with REDIRECT_URL)
|
||||
: "${REDIRECT_URL:=}" # optional: where the base domain 301s non-webfinger traffic
|
||||
|
||||
log() { printf '\033[1;32m[+]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
|
||||
@@ -121,7 +124,7 @@ else
|
||||
die "No embedded archive found. Run build.sh to embed deployment files."
|
||||
fi
|
||||
|
||||
for f in docker-compose.yml Caddyfile .env.example; do
|
||||
for f in docker-compose.yml Caddyfile Caddyfile.webfinger .env.example; do
|
||||
[[ -f "$SCRIPT_DIR/$f" ]] || die "Embedded archive missing $f"
|
||||
done
|
||||
|
||||
@@ -154,7 +157,8 @@ open_web_ports
|
||||
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/Caddyfile" "$STACK_DIR/Caddyfile"
|
||||
# (Caddyfile is assembled below, after .env, so the optional WebFinger block
|
||||
# can be appended when BASE_DOMAIN is set.)
|
||||
|
||||
ENV_FILE="$STACK_DIR/.env"
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
@@ -165,6 +169,8 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
||||
-e "s|^ACME_EMAIL=.*|ACME_EMAIL=${ACME_EMAIL}|" \
|
||||
-e "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$(openssl rand -base64 32)|" \
|
||||
-e "s|^ANUBIS_PID_KEY=.*|ANUBIS_PID_KEY=$(openssl rand -hex 32)|" \
|
||||
-e "s|^BASE_DOMAIN=.*|BASE_DOMAIN=${BASE_DOMAIN}|" \
|
||||
-e "s|^REDIRECT_URL=.*|REDIRECT_URL=${REDIRECT_URL}|" \
|
||||
"$ENV_FILE"
|
||||
else
|
||||
log ".env exists; leaving secrets alone."
|
||||
@@ -177,6 +183,20 @@ for var in POCKETID_DOMAIN ACME_EMAIL ENCRYPTION_KEY ANUBIS_PID_KEY; do
|
||||
done
|
||||
(( ${#missing[@]} == 0 )) || die "Missing values in $ENV_FILE: ${missing[*]}"
|
||||
|
||||
# Assemble the deployed Caddyfile each run: base (pocket-id) + the optional
|
||||
# WebFinger block when BASE_DOMAIN is set in .env. Regenerated every run so
|
||||
# edits to .env propagate.
|
||||
install -m 0640 "$SCRIPT_DIR/Caddyfile" "$STACK_DIR/Caddyfile"
|
||||
bd="$(sed -n 's/^BASE_DOMAIN=//p' "$ENV_FILE")"
|
||||
ru="$(sed -n 's/^REDIRECT_URL=//p' "$ENV_FILE")"
|
||||
if [[ -n "$bd" ]]; then
|
||||
[[ -n "$ru" ]] || die "BASE_DOMAIN is set but REDIRECT_URL is empty in $ENV_FILE; set both or neither."
|
||||
log "WebFinger enabled -- serving /.well-known/webfinger at ${bd}."
|
||||
cat "$SCRIPT_DIR/Caddyfile.webfinger" >> "$STACK_DIR/Caddyfile"
|
||||
else
|
||||
log "No BASE_DOMAIN -- pocket-id only (use the webfinger deployment for OIDC discovery)."
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Bring up the stack
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -243,50 +263,62 @@ EOF
|
||||
# __ARCHIVE_BELOW__ marker is the embedded tar.gz payload (base64).
|
||||
exit 0
|
||||
__ARCHIVE_BELOW__
|
||||
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
|
||||
2B7bY/v29m9v1GZbACgAAA==
|
||||
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
|
||||
|
||||
@@ -35,6 +35,10 @@ services:
|
||||
environment:
|
||||
POCKETID_DOMAIN: "${POCKETID_DOMAIN}"
|
||||
ACME_EMAIL: "${ACME_EMAIL}"
|
||||
# Only used by the optional WebFinger block (appended by deploy.sh when
|
||||
# BASE_DOMAIN is set). Empty otherwise -- harmless.
|
||||
BASE_DOMAIN: "${BASE_DOMAIN:-}"
|
||||
REDIRECT_URL: "${REDIRECT_URL:-}"
|
||||
depends_on:
|
||||
- pocket-id
|
||||
healthcheck:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#cloud-config
|
||||
#
|
||||
# WebFinger + redirect — unattended deploy on a fresh Alpine host.
|
||||
# WebFinger + redirect — harden SSH, then deploy, on a fresh Alpine host.
|
||||
#
|
||||
# Fill in REPO_URL and the values in the runcmd block, then paste this as the
|
||||
# instance user-data. DNS for BASE_DOMAIN must point at this host and ports
|
||||
@@ -15,15 +15,20 @@ runcmd:
|
||||
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
|
||||
|
||||
# Optional: install shared admin SSH keys from globals/ for root before deploy
|
||||
# . scripts/lib.sh && load_globals \
|
||||
# && install -d -m700 /root/.ssh && resolve_ssh_keys >> /root/.ssh/authorized_keys
|
||||
|
||||
# Optional: harden SSH first (prints a fresh root key — capture it!)
|
||||
# SKIP_PROMPTS=1 FORCE=1 bash scripts/harden-ssh.sh
|
||||
# Harden SSH on this fresh VM: PQ KEX, key-only auth, sshguard. Seeds root
|
||||
# from globals/authorized_keys (or SSH_KEYS_URL). NOTE: harden-ssh also
|
||||
# prints a generated root key to the serial console — capture it, or rely
|
||||
# on the seeded keys.
|
||||
if [ "$HARDEN_SSH" = 1 ]; then
|
||||
SSH_PORT="$SSH_PORT" ALLOWED_IP="$ALLOWED_IP" SKIP_PROMPTS=1 FORCE=1 \
|
||||
bash scripts/harden-ssh.sh
|
||||
fi
|
||||
|
||||
BASE_DOMAIN=example.com \
|
||||
ISSUER_URL=https://auth.example.com \
|
||||
|
||||
Reference in New Issue
Block a user