Files
57_Wolve 7faa9098de 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 with AUTO_REBOOT=idle (reboots only when no SSH active) and
  opt-in Alpine stable-branch upgrades
- generic + per-deployment cloud-init; Gitea release workflow on tag
- README/LICENSE/.gitignore/.gitattributes (force LF); repo URLs -> Gitea
2026-06-12 14:56:02 -05:00

299 lines
11 KiB
Bash

#!/usr/bin/env bash
#
# install-simplex.sh
#
# Master installer for SimpleX relay deployment. Designed for cloud-init but
# works as a standalone script. Fetches deployment scripts from git, runs the
# complete setup sequence, and creates an initial encrypted backup.
#
# This script:
# 1. Fetches deployment scripts from your git repo
# 2. Runs harden-ssh.sh (PQ KEX, Ed25519, firewall)
# 3. Runs deploy-simplex.sh (SMP + XFTP + Tor)
# 4. Runs backup.sh with your age public key
# 5. Cleans up CA keys from disk (they're now only in the encrypted backup)
# 6. Reports final status and backup location
#
# Usage as cloud-init user-data:
# #cloud-config
# runcmd:
# - |
# curl -fsSL https://git.anomalous.dev/57_Wolve/automations/raw/branch/main/deployments/simplex/install-simplex.sh | \
# REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git \
# DOMAIN=relay.example.com \
# ACME_EMAIL=admin@example.com \
# XFTP_QUOTA=100gb \
# SSH_PORT=2222 \
# bash
#
# Usage as standalone script:
# curl -fsSL https://git.anomalous.dev/57_Wolve/automations/raw/branch/main/deployments/simplex/install-simplex.sh > install-simplex.sh
# REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git \
# DOMAIN=relay.example.com \
# ACME_EMAIL=admin@example.com \
# bash install-simplex.sh
#
# Required git repo structure (this monorepo):
# automations/
# ├── scripts/
# │ └── harden-ssh.sh # generic, run-anywhere
# ├── deployments/simplex/
# │ ├── deploy-simplex.sh
# │ ├── backup.sh
# │ └── restore.sh # optional
# └── globals/
# └── age-pubkey.txt # your age public key(s), one per line
set -euo pipefail
# ============================================================================
# CONFIG
# ============================================================================
# Git repository containing the deployment scripts and age public key
: "${REPO_URL:=}" # REQUIRED: git repo URL
# SimpleX deployment config
: "${DOMAIN:=}" # REQUIRED: apex domain (uses smp.DOMAIN, xftp.DOMAIN)
: "${ACME_EMAIL:=}" # REQUIRED: Let's Encrypt email
: "${XFTP_QUOTA:=50gb}" # XFTP disk quota
: "${SSH_PORT:=22}" # SSH port (recommend changing from default)
: "${KEY_TYPE:=rsa4096}" # Caddy TLS key type
: "${SMP_PASS:=}" # optional: SMP queue creation password
: "${XFTP_PASS:=}" # optional: XFTP upload password
# Git and installation options
: "${REPO_BRANCH:=main}" # git branch to fetch
: "${HARDEN_PATH:=scripts}" # path within repo to the generic harden-ssh.sh
: "${SIMPLEX_PATH:=deployments/simplex}" # path within repo to the simplex scripts
: "${AGE_PUBKEY_FILE:=globals/age-pubkey.txt}" # path within repo to age public key
: "${INSTALL_DIR:=/opt/simplex-deploy}" # where to clone the repo
: "${ALLOWED_IP:=}" # optional: IP to whitelist in sshguard
: "${AUTO_BACKUP:=1}" # set to 0 to skip initial backup
: "${REMOVE_CA_KEYS:=1}" # set to 0 to keep CA keys on disk
# Behavior flags
: "${SKIP_PROMPTS:=0}" # set to 1 for non-interactive operation
: "${DEBUG:=0}" # set to 1 for verbose output
# ============================================================================
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."
[[ -f /etc/alpine-release ]] || die "This script targets Alpine Linux."
if [[ "$DEBUG" == "1" ]]; then
set -x
fi
# ----------------------------------------------------------------------------
# 1. Validate required parameters
# ----------------------------------------------------------------------------
[[ -n "$REPO_URL" ]] || die "Set REPO_URL=https://git.anomalous.dev/57_Wolve/automations.git"
[[ -n "$DOMAIN" ]] || die "Set DOMAIN=your.domain.com"
[[ -n "$ACME_EMAIL" ]] || die "Set ACME_EMAIL=admin@your.domain.com"
log "SimpleX relay installer"
log "Repo: $REPO_URL"
log "Domain: $DOMAIN (will create smp.$DOMAIN, xftp.$DOMAIN)"
log "SSH port: $SSH_PORT"
if [[ "$SKIP_PROMPTS" != "1" ]]; then
cat <<EOF
This will install a complete SimpleX relay server on this host:
• SSH hardening with PQ KEX and Ed25519 keys
• awall firewall with minimal attack surface
• SMP + XFTP servers with Tor hidden services
• Caddy reverse proxy with Let's Encrypt TLS
• Initial encrypted backup of all server keys
The process will generate new server keys and may change your SSH config.
Only run this on a fresh Alpine install intended as a SimpleX relay.
Continue? [y/N]
EOF
read -r ans
[[ "${ans,,}" =~ ^(y|yes)$ ]] || die "Aborted."
fi
# ----------------------------------------------------------------------------
# 2. Install git and fetch deployment repo
# ----------------------------------------------------------------------------
log "Installing git and fetching deployment scripts..."
apk add -q git
rm -rf "$INSTALL_DIR"
git clone --depth 1 --branch "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR"
# Verify required files exist. harden-ssh.sh is generic and lives under
# scripts/; the simplex scripts live under deployments/simplex/.
HARDEN_DIR="$INSTALL_DIR/$HARDEN_PATH"
SIMPLEX_DIR="$INSTALL_DIR/$SIMPLEX_PATH"
verify_script() { # <dir> <relpath-for-messages> <name>
local path="$1/$3"
[[ -f "$path" ]] || die "Required script not found: $2/$3"
[[ -x "$path" ]] || chmod +x "$path"
}
verify_script "$HARDEN_DIR" "$HARDEN_PATH" "harden-ssh.sh"
verify_script "$SIMPLEX_DIR" "$SIMPLEX_PATH" "deploy-simplex.sh"
verify_script "$SIMPLEX_DIR" "$SIMPLEX_PATH" "backup.sh"
# Check for age public key file
AGE_PUBKEY_PATH="$INSTALL_DIR/$AGE_PUBKEY_FILE"
if [[ "$AUTO_BACKUP" == "1" ]]; then
[[ -f "$AGE_PUBKEY_PATH" ]] || die "Age public key file not found: $AGE_PUBKEY_FILE"
# Validate it contains what looks like age public keys
if ! grep -q "^age1" "$AGE_PUBKEY_PATH"; then
die "Age public key file doesn't contain valid age1... public keys"
fi
fi
log "Repository cloned to: $INSTALL_DIR"
# ----------------------------------------------------------------------------
# 3. Run SSH hardening
# ----------------------------------------------------------------------------
log "Step 1/3: SSH hardening..."
SSH_ENV=""
[[ -n "$SSH_PORT" ]] && SSH_ENV="${SSH_ENV} SSH_PORT=$SSH_PORT"
[[ -n "$ALLOWED_IP" ]] && SSH_ENV="${SSH_ENV} ALLOWED_IP=$ALLOWED_IP"
[[ "$SKIP_PROMPTS" == "1" ]] && SSH_ENV="${SSH_ENV} FORCE=1"
# Capture the private key output from harden-ssh.sh
HARDEN_OUTPUT=$(env $SSH_ENV "$HARDEN_DIR/harden-ssh.sh" 2>&1) || {
echo "$HARDEN_OUTPUT" >&2
die "SSH hardening failed"
}
# Extract the private key from the output for later display
SSH_PRIVATE_KEY=$(echo "$HARDEN_OUTPUT" | sed -n '/BEGIN ROOT PRIVATE KEY/,/END ROOT PRIVATE KEY/p')
SSH_PUBLIC_KEY=$(echo "$HARDEN_OUTPUT" | grep "Public key (already in" | sed 's/.*: //')
HOST_FINGERPRINT=$(echo "$HARDEN_OUTPUT" | grep "Host fingerprint" | tail -1)
log "SSH hardening completed"
# ----------------------------------------------------------------------------
# 4. Run SimpleX deployment
# ----------------------------------------------------------------------------
log "Step 2/3: SimpleX deployment..."
DEPLOY_ENV=""
DEPLOY_ENV="${DEPLOY_ENV} DOMAIN=$DOMAIN"
DEPLOY_ENV="${DEPLOY_ENV} ACME_EMAIL=$ACME_EMAIL"
DEPLOY_ENV="${DEPLOY_ENV} XFTP_QUOTA=$XFTP_QUOTA"
DEPLOY_ENV="${DEPLOY_ENV} SSH_PORT=$SSH_PORT"
DEPLOY_ENV="${DEPLOY_ENV} KEY_TYPE=$KEY_TYPE"
[[ -n "$SMP_PASS" ]] && DEPLOY_ENV="${DEPLOY_ENV} SMP_PASS=$SMP_PASS"
[[ -n "$XFTP_PASS" ]] && DEPLOY_ENV="${DEPLOY_ENV} XFTP_PASS=$XFTP_PASS"
env $DEPLOY_ENV "$SIMPLEX_DIR/deploy-simplex.sh" || die "SimpleX deployment failed"
log "SimpleX deployment completed"
# ----------------------------------------------------------------------------
# 5. Create initial encrypted backup
# ----------------------------------------------------------------------------
if [[ "$AUTO_BACKUP" == "1" ]]; then
log "Step 3/3: Creating initial encrypted backup..."
# Wait a bit for the services to fully initialize
sleep 10
BACKUP_ENV="AGE_RECIPIENT_FILE=$AGE_PUBKEY_PATH"
env $BACKUP_ENV "$SIMPLEX_DIR/backup.sh" || die "Backup creation failed"
log "Initial backup created"
# Remove CA keys from disk now that they're safely backed up
if [[ "$REMOVE_CA_KEYS" == "1" ]]; then
log "Removing CA private keys from disk (they're now only in the encrypted backup)..."
rm -f /opt/simplex/smp_configs/ca.key /opt/simplex/xftp_configs/ca.key
log "CA keys removed from disk"
fi
else
log "Step 3/3: Skipped (AUTO_BACKUP=0)"
fi
# ----------------------------------------------------------------------------
# 6. Final status report
# ----------------------------------------------------------------------------
sleep 5 # Let services settle
cat <<EOF
================================================================
SIMPLEX RELAY INSTALLATION COMPLETE
================================================================
Your SimpleX relay is now running at:
Domain: $DOMAIN
SMP: smp.$DOMAIN:5223
XFTP: xftp.$DOMAIN:5443
SSH: port $SSH_PORT
EOF
# Show server addresses if available
if [[ -f /opt/simplex/print-addresses.sh ]]; then
cd /opt/simplex && ./print-addresses.sh 2>/dev/null || {
log "Server addresses not yet ready; check with: cd /opt/simplex && ./print-addresses.sh"
}
else
log "Server addresses script not found"
fi
# Show backup location
LATEST_BACKUP=$(find /tmp -name 'simplex-backup-*.tar.gz.age' -type f -printf '%T@ %p\n' 2>/dev/null | sort -nr | head -1 | cut -d' ' -f2-)
if [[ -n "$LATEST_BACKUP" && "$AUTO_BACKUP" == "1" ]]; then
BACKUP_SIZE=$(stat -c%s "$LATEST_BACKUP" 2>/dev/null || echo "unknown")
cat <<EOF
BACKUP CREATED:
File: $LATEST_BACKUP
Size: $BACKUP_SIZE bytes
IMPORTANT: Download this backup immediately and store it securely.
It contains all server private keys. The CA keys have been removed
from disk and exist only in this encrypted backup.
Download with:
scp -i ~/.ssh/your_key -P $SSH_PORT root@$DOMAIN:$LATEST_BACKUP ./
EOF
fi
cat <<EOF
IMPORTANT - SAVE THIS SSH PRIVATE KEY:
$SSH_PRIVATE_KEY
Connect with:
ssh -i ~/.ssh/id_ed25519_simplex -p $SSH_PORT root@$DOMAIN
$HOST_FINGERPRINT
NEXT STEPS:
1. Save the SSH private key above to your local machine
2. Download the encrypted backup from $LATEST_BACKUP
3. Test SSH access from another terminal
4. Add the server to your SimpleX app:
Settings -> Network & Servers -> Add servers -> paste the addresses above
5. Test that contacts can reach your relay
================================================================
EOF
# Clean up the git repo (optional - contains no secrets)
if [[ "${CLEANUP_REPO:-1}" == "1" ]]; then
rm -rf "$INSTALL_DIR"
log "Cleaned up installation directory"
fi
log "Installation complete!"