7faa9098de
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
299 lines
11 KiB
Bash
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!"
|