- Add secubox-landing script to generate landing pages from HAProxy vhosts - Integrate landing command into secubox CLI - Add boot hook to regenerate landing pages on startup - Fix HAProxy multi-cert SNI using crt-list instead of directory mode - Fix backend IPs from 127.0.0.1 to 192.168.255.1 for LXC compatibility - Auto-convert localhost IPs in RPCD handler and CLI tools Landing page features: - Groups all services by zone with stats header - Shows SSL certificate status per domain - Categorizes by type: Streamlit, Blog, Admin, Media, Dev, etc. - Regenerates at boot (30s after startup) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
992 lines
24 KiB
Bash
992 lines
24 KiB
Bash
#!/bin/sh
|
|
# SimpleX Chat Server Control Script for SecuBox
|
|
# Manages SMP and XFTP servers in LXC container
|
|
|
|
VERSION="1.0.0"
|
|
SIMPLEX_DIR="/srv/simplex"
|
|
CONTAINER_NAME="simplex"
|
|
# Use LXC's configured path (default: /srv/lxc on OpenWrt)
|
|
LXC_PATH=$(grep -s "lxc.lxcpath" /etc/lxc/lxc.conf 2>/dev/null | cut -d'=' -f2 | tr -d ' ' || echo "/srv/lxc")
|
|
CONTAINER_PATH="$LXC_PATH/$CONTAINER_NAME"
|
|
SMP_DIR="$SIMPLEX_DIR/smp"
|
|
XFTP_DIR="$SIMPLEX_DIR/xftp"
|
|
CERTS_DIR="$SIMPLEX_DIR/certs"
|
|
|
|
# SimpleX release information
|
|
SIMPLEX_VERSION="v6.0.4"
|
|
SIMPLEX_BASE_URL="https://github.com/simplex-chat/simplexmq/releases/download"
|
|
|
|
# Colors for terminal output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[SIMPLEX]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
# ============================================================================
|
|
# UCI Configuration Helpers
|
|
# ============================================================================
|
|
|
|
uci_get() { uci -q get simplex.$1; }
|
|
uci_set() { uci -q set simplex.$1="$2" && uci commit simplex; }
|
|
|
|
load_config() {
|
|
# Main settings
|
|
ENABLED=$(uci_get main.enabled || echo "0")
|
|
DATA_PATH=$(uci_get main.data_path || echo "$SIMPLEX_DIR")
|
|
MEMORY_LIMIT=$(uci_get main.memory_limit || echo "256M")
|
|
|
|
# SMP settings
|
|
SMP_ENABLED=$(uci_get smp.enabled || echo "1")
|
|
SMP_PORT=$(uci_get smp.port || echo "5223")
|
|
SMP_CONTROL_PORT=$(uci_get smp.control_port || echo "5224")
|
|
SMP_HOSTNAME=$(uci_get smp.hostname)
|
|
SMP_STORE_LOG=$(uci_get smp.store_log || echo "1")
|
|
SMP_QUEUE_PASSWORD=$(uci_get smp.queue_password)
|
|
|
|
# XFTP settings
|
|
XFTP_ENABLED=$(uci_get xftp.enabled || echo "1")
|
|
XFTP_PORT=$(uci_get xftp.port || echo "443")
|
|
XFTP_CONTROL_PORT=$(uci_get xftp.control_port || echo "5225")
|
|
XFTP_HOSTNAME=$(uci_get xftp.hostname)
|
|
XFTP_STORAGE_QUOTA=$(uci_get xftp.storage_quota || echo "10G")
|
|
XFTP_FILE_EXPIRY=$(uci_get xftp.file_expiry || echo "48h")
|
|
XFTP_CREATE_PASSWORD=$(uci_get xftp.create_password)
|
|
|
|
# TLS settings
|
|
USE_LETSENCRYPT=$(uci_get tls.use_letsencrypt || echo "0")
|
|
CERT_PATH=$(uci_get tls.cert_path || echo "$CERTS_DIR")
|
|
TLS_DOMAIN=$(uci_get tls.domain)
|
|
TLS_EMAIL=$(uci_get tls.email)
|
|
|
|
# Use hostname from TLS domain if not set specifically
|
|
[ -z "$SMP_HOSTNAME" ] && SMP_HOSTNAME="$TLS_DOMAIN"
|
|
[ -z "$XFTP_HOSTNAME" ] && XFTP_HOSTNAME="$TLS_DOMAIN"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Architecture Detection
|
|
# ============================================================================
|
|
|
|
detect_arch() {
|
|
local machine=$(uname -m)
|
|
case "$machine" in
|
|
aarch64|arm64)
|
|
echo "aarch64"
|
|
;;
|
|
x86_64|amd64)
|
|
echo "x86_64"
|
|
;;
|
|
*)
|
|
error "Unsupported architecture: $machine"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# LXC Container Management
|
|
# ============================================================================
|
|
|
|
container_exists() {
|
|
lxc-info -n "$CONTAINER_NAME" >/dev/null 2>&1
|
|
}
|
|
|
|
container_running() {
|
|
lxc-info -n "$CONTAINER_NAME" -s 2>/dev/null | grep -q "RUNNING"
|
|
}
|
|
|
|
create_container() {
|
|
log "Creating Alpine Linux LXC container..."
|
|
|
|
# Create container directory
|
|
mkdir -p "$CONTAINER_PATH/rootfs"
|
|
|
|
# Download Alpine minirootfs (skip if already present)
|
|
local arch=$(detect_arch)
|
|
local alpine_version="3.19"
|
|
local alpine_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version}/releases/${arch}/alpine-minirootfs-${alpine_version}.0-${arch}.tar.gz"
|
|
|
|
if [ -f /tmp/alpine-rootfs.tar.gz ]; then
|
|
log "Using existing Alpine rootfs from /tmp/alpine-rootfs.tar.gz"
|
|
else
|
|
log "Downloading Alpine Linux minirootfs..."
|
|
if ! wget -q -O /tmp/alpine-rootfs.tar.gz "$alpine_url"; then
|
|
error "Failed to download Alpine rootfs"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Extract rootfs
|
|
tar -xzf /tmp/alpine-rootfs.tar.gz -C "$CONTAINER_PATH/rootfs"
|
|
rm -f /tmp/alpine-rootfs.tar.gz
|
|
|
|
# Create LXC config
|
|
cat > "$CONTAINER_PATH/config" << EOF
|
|
# SimpleX Chat LXC Container Configuration
|
|
|
|
lxc.uts.name = $CONTAINER_NAME
|
|
lxc.arch = linux64
|
|
|
|
# Rootfs
|
|
lxc.rootfs.path = dir:$CONTAINER_PATH/rootfs
|
|
|
|
# Network - use host networking for direct port access
|
|
lxc.net.0.type = none
|
|
|
|
# Resource limits
|
|
lxc.cgroup2.memory.max = $MEMORY_LIMIT
|
|
|
|
# Mount points for persistent data
|
|
lxc.mount.entry = $DATA_PATH srv/simplex none bind,create=dir 0 0
|
|
|
|
# Capabilities
|
|
lxc.cap.drop = sys_admin sys_module mac_admin mac_override
|
|
|
|
# Console
|
|
lxc.tty.max = 1
|
|
lxc.pty.max = 1
|
|
|
|
# Auto start
|
|
lxc.start.auto = 0
|
|
EOF
|
|
|
|
# Setup container init
|
|
cat > "$CONTAINER_PATH/rootfs/etc/inittab" << 'EOF'
|
|
::sysinit:/sbin/openrc sysinit
|
|
::sysinit:/sbin/openrc boot
|
|
::wait:/sbin/openrc default
|
|
::ctrlaltdel:/sbin/reboot
|
|
::shutdown:/sbin/openrc shutdown
|
|
EOF
|
|
|
|
# Configure networking in container
|
|
cat > "$CONTAINER_PATH/rootfs/etc/resolv.conf" << EOF
|
|
nameserver 127.0.0.1
|
|
nameserver 1.1.1.1
|
|
EOF
|
|
|
|
# Install required packages in container
|
|
cat > "$CONTAINER_PATH/rootfs/tmp/setup.sh" << 'SETUP'
|
|
#!/bin/sh
|
|
apk update
|
|
apk add --no-cache ca-certificates openssl curl
|
|
mkdir -p /srv/simplex/smp /srv/simplex/xftp /srv/simplex/certs
|
|
SETUP
|
|
chmod +x "$CONTAINER_PATH/rootfs/tmp/setup.sh"
|
|
|
|
log "Container created successfully"
|
|
}
|
|
|
|
start_container() {
|
|
if ! container_exists; then
|
|
error "Container does not exist. Run: simplexctl install"
|
|
return 1
|
|
fi
|
|
|
|
if container_running; then
|
|
log "Container already running"
|
|
return 0
|
|
fi
|
|
|
|
log "Starting container..."
|
|
lxc-start -n "$CONTAINER_NAME" -d
|
|
|
|
# Wait for container to be ready
|
|
local timeout=30
|
|
while [ $timeout -gt 0 ]; do
|
|
if container_running; then
|
|
log "Container started"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
timeout=$((timeout - 1))
|
|
done
|
|
|
|
error "Container failed to start"
|
|
return 1
|
|
}
|
|
|
|
stop_container() {
|
|
if container_running; then
|
|
log "Stopping container..."
|
|
lxc-stop -n "$CONTAINER_NAME" -t 10
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# SimpleX Binary Management
|
|
# ============================================================================
|
|
|
|
download_binaries() {
|
|
log "Downloading SimpleX binaries (version $SIMPLEX_VERSION)..."
|
|
|
|
local arch=$(detect_arch)
|
|
local bin_suffix=""
|
|
|
|
case "$arch" in
|
|
aarch64) bin_suffix="ubuntu-20_04-aarch64" ;;
|
|
x86_64) bin_suffix="ubuntu-20_04-x86-64" ;;
|
|
esac
|
|
|
|
local smp_url="$SIMPLEX_BASE_URL/$SIMPLEX_VERSION/smp-server-$bin_suffix"
|
|
local xftp_url="$SIMPLEX_BASE_URL/$SIMPLEX_VERSION/xftp-server-$bin_suffix"
|
|
|
|
mkdir -p "$DATA_PATH/bin"
|
|
|
|
# Download SMP server
|
|
log "Downloading SMP server..."
|
|
if ! wget -q --show-progress -O "$DATA_PATH/bin/smp-server" "$smp_url"; then
|
|
error "Failed to download SMP server"
|
|
return 1
|
|
fi
|
|
chmod +x "$DATA_PATH/bin/smp-server"
|
|
|
|
# Download XFTP server
|
|
log "Downloading XFTP server..."
|
|
if ! wget -q --show-progress -O "$DATA_PATH/bin/xftp-server" "$xftp_url"; then
|
|
error "Failed to download XFTP server"
|
|
return 1
|
|
fi
|
|
chmod +x "$DATA_PATH/bin/xftp-server"
|
|
|
|
log "Binaries downloaded successfully"
|
|
}
|
|
|
|
# ============================================================================
|
|
# TLS Certificate Management
|
|
# ============================================================================
|
|
|
|
generate_self_signed_certs() {
|
|
local hostname="$1"
|
|
[ -z "$hostname" ] && hostname="simplex.local"
|
|
|
|
log "Generating self-signed TLS certificates for $hostname..."
|
|
|
|
mkdir -p "$CERT_PATH"
|
|
|
|
# Generate CA key and cert
|
|
openssl genrsa -out "$CERT_PATH/ca.key" 4096 2>/dev/null
|
|
openssl req -new -x509 -days 3650 -key "$CERT_PATH/ca.key" \
|
|
-out "$CERT_PATH/ca.crt" -subj "/CN=SimpleX CA" 2>/dev/null
|
|
|
|
# Generate server key and CSR
|
|
openssl genrsa -out "$CERT_PATH/server.key" 4096 2>/dev/null
|
|
openssl req -new -key "$CERT_PATH/server.key" \
|
|
-out "$CERT_PATH/server.csr" -subj "/CN=$hostname" 2>/dev/null
|
|
|
|
# Create server certificate
|
|
cat > "$CERT_PATH/server.ext" << EOF
|
|
authorityKeyIdentifier=keyid,issuer
|
|
basicConstraints=CA:FALSE
|
|
keyUsage = digitalSignature, keyEncipherment
|
|
extendedKeyUsage = serverAuth
|
|
subjectAltName = DNS:$hostname
|
|
EOF
|
|
|
|
openssl x509 -req -in "$CERT_PATH/server.csr" -CA "$CERT_PATH/ca.crt" \
|
|
-CAkey "$CERT_PATH/ca.key" -CAcreateserial -out "$CERT_PATH/server.crt" \
|
|
-days 365 -extfile "$CERT_PATH/server.ext" 2>/dev/null
|
|
|
|
# Cleanup
|
|
rm -f "$CERT_PATH/server.csr" "$CERT_PATH/server.ext"
|
|
|
|
log "Certificates generated at $CERT_PATH"
|
|
}
|
|
|
|
init_letsencrypt() {
|
|
local domain="$1"
|
|
local email="$2"
|
|
|
|
if [ -z "$domain" ]; then
|
|
error "Domain required for Let's Encrypt"
|
|
return 1
|
|
fi
|
|
|
|
log "Initializing Let's Encrypt for $domain..."
|
|
|
|
# Use acme.sh if available, or certbot
|
|
if command -v acme.sh >/dev/null 2>&1; then
|
|
acme.sh --issue -d "$domain" --standalone --keylength 4096 \
|
|
--cert-file "$CERT_PATH/server.crt" \
|
|
--key-file "$CERT_PATH/server.key" \
|
|
--fullchain-file "$CERT_PATH/fullchain.crt"
|
|
else
|
|
error "acme.sh not found. Install with: opkg install acme"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Server Configuration
|
|
# ============================================================================
|
|
|
|
init_smp_server() {
|
|
log "Initializing SMP server..."
|
|
|
|
mkdir -p "$SMP_DIR"
|
|
|
|
# Initialize server if not already done
|
|
if [ ! -f "$SMP_DIR/smp-server.ini" ]; then
|
|
local hostname="${SMP_HOSTNAME:-$(hostname)}"
|
|
|
|
# Create SMP config
|
|
cat > "$SMP_DIR/smp-server.ini" << EOF
|
|
[STORE_LOG]
|
|
enable: ${SMP_STORE_LOG}
|
|
restore_messages: on
|
|
|
|
[AUTH]
|
|
new_queues: ${SMP_QUEUE_PASSWORD:+on}
|
|
|
|
[TRANSPORT]
|
|
host: $hostname
|
|
port: $SMP_PORT
|
|
control_port: ${SMP_CONTROL_PORT}
|
|
websockets: off
|
|
|
|
[SERVER]
|
|
information: SimpleX SMP Server
|
|
source_code: https://github.com/simplex-chat/simplexmq
|
|
EOF
|
|
|
|
# Add password if set
|
|
if [ -n "$SMP_QUEUE_PASSWORD" ]; then
|
|
echo "create_password: $SMP_QUEUE_PASSWORD" >> "$SMP_DIR/smp-server.ini"
|
|
fi
|
|
fi
|
|
|
|
# Generate server keys if not present
|
|
if [ ! -f "$SMP_DIR/server.key" ]; then
|
|
log "Generating SMP server keys..."
|
|
if [ -x "$DATA_PATH/bin/smp-server" ]; then
|
|
cd "$SMP_DIR"
|
|
"$DATA_PATH/bin/smp-server" init --store-log
|
|
fi
|
|
fi
|
|
|
|
log "SMP server initialized"
|
|
}
|
|
|
|
init_xftp_server() {
|
|
log "Initializing XFTP server..."
|
|
|
|
mkdir -p "$XFTP_DIR/files"
|
|
|
|
# Parse storage quota
|
|
local quota_bytes=$(echo "$XFTP_STORAGE_QUOTA" | \
|
|
sed -e 's/G/*1024*1024*1024/' -e 's/M/*1024*1024/' -e 's/K/*1024/' | bc)
|
|
|
|
# Create XFTP config
|
|
if [ ! -f "$XFTP_DIR/xftp-server.ini" ]; then
|
|
local hostname="${XFTP_HOSTNAME:-$(hostname)}"
|
|
|
|
cat > "$XFTP_DIR/xftp-server.ini" << EOF
|
|
[STORE_LOG]
|
|
enable: on
|
|
file_path: $XFTP_DIR/files
|
|
|
|
[AUTH]
|
|
new_files: ${XFTP_CREATE_PASSWORD:+on}
|
|
|
|
[TRANSPORT]
|
|
host: $hostname
|
|
port: $XFTP_PORT
|
|
control_port: ${XFTP_CONTROL_PORT}
|
|
|
|
[SERVER]
|
|
information: SimpleX XFTP Server
|
|
source_code: https://github.com/simplex-chat/simplexmq
|
|
|
|
[QUOTA]
|
|
storage_quota: $quota_bytes
|
|
file_expiration: $XFTP_FILE_EXPIRY
|
|
EOF
|
|
|
|
# Add password if set
|
|
if [ -n "$XFTP_CREATE_PASSWORD" ]; then
|
|
echo "create_password: $XFTP_CREATE_PASSWORD" >> "$XFTP_DIR/xftp-server.ini"
|
|
fi
|
|
fi
|
|
|
|
# Generate server keys if not present
|
|
if [ ! -f "$XFTP_DIR/server.key" ]; then
|
|
log "Generating XFTP server keys..."
|
|
if [ -x "$DATA_PATH/bin/xftp-server" ]; then
|
|
cd "$XFTP_DIR"
|
|
"$DATA_PATH/bin/xftp-server" init
|
|
fi
|
|
fi
|
|
|
|
log "XFTP server initialized"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Service Control
|
|
# ============================================================================
|
|
|
|
service_run() {
|
|
local service="$1"
|
|
load_config
|
|
|
|
case "$service" in
|
|
smp)
|
|
if [ -x "$DATA_PATH/bin/smp-server" ]; then
|
|
cd "$SMP_DIR"
|
|
exec "$DATA_PATH/bin/smp-server" start
|
|
else
|
|
error "SMP server binary not found"
|
|
exit 1
|
|
fi
|
|
;;
|
|
xftp)
|
|
if [ -x "$DATA_PATH/bin/xftp-server" ]; then
|
|
cd "$XFTP_DIR"
|
|
exec "$DATA_PATH/bin/xftp-server" start
|
|
else
|
|
error "XFTP server binary not found"
|
|
exit 1
|
|
fi
|
|
;;
|
|
*)
|
|
error "Unknown service: $service"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
stop_services() {
|
|
# Send SIGTERM to any running servers
|
|
pgrep smp-server && pkill smp-server
|
|
pgrep xftp-server && pkill xftp-server
|
|
}
|
|
|
|
reload_config() {
|
|
load_config
|
|
init_smp_server
|
|
init_xftp_server
|
|
log "Configuration reloaded"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Server Addresses
|
|
# ============================================================================
|
|
|
|
get_server_fingerprint() {
|
|
local key_file="$1"
|
|
if [ -f "$key_file" ]; then
|
|
# Extract base64-encoded fingerprint from key
|
|
openssl x509 -in "$key_file" -noout -fingerprint -sha256 2>/dev/null | \
|
|
sed 's/.*=//' | tr -d ':' | xxd -r -p | base64 | tr '+/' '-_' | tr -d '='
|
|
fi
|
|
}
|
|
|
|
show_addresses() {
|
|
load_config
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " SimpleX Server Addresses"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# SMP address
|
|
if [ "$SMP_ENABLED" = "1" ] && [ -f "$SMP_DIR/fingerprint" ]; then
|
|
local smp_fp=$(cat "$SMP_DIR/fingerprint" 2>/dev/null)
|
|
local smp_host="${SMP_HOSTNAME:-$(hostname)}"
|
|
echo "SMP Server:"
|
|
echo " smp://${smp_fp}@${smp_host}:${SMP_PORT}"
|
|
echo ""
|
|
fi
|
|
|
|
# XFTP address
|
|
if [ "$XFTP_ENABLED" = "1" ] && [ -f "$XFTP_DIR/fingerprint" ]; then
|
|
local xftp_fp=$(cat "$XFTP_DIR/fingerprint" 2>/dev/null)
|
|
local xftp_host="${XFTP_HOSTNAME:-$(hostname)}"
|
|
echo "XFTP Server:"
|
|
echo " xftp://${xftp_fp}@${xftp_host}:${XFTP_PORT}"
|
|
echo ""
|
|
fi
|
|
|
|
echo "Add these addresses to your SimpleX Chat app"
|
|
echo "under Settings > Network & Servers"
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# Firewall Configuration
|
|
# ============================================================================
|
|
|
|
configure_firewall() {
|
|
load_config
|
|
log "Configuring firewall..."
|
|
|
|
# SMP port
|
|
if [ "$SMP_ENABLED" = "1" ]; then
|
|
if ! uci show firewall 2>/dev/null | grep -q "SimpleX-SMP"; then
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='SimpleX-SMP'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port="$SMP_PORT"
|
|
uci set firewall.@rule[-1].proto='tcp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
uci set firewall.@rule[-1].enabled='1'
|
|
fi
|
|
fi
|
|
|
|
# XFTP port
|
|
if [ "$XFTP_ENABLED" = "1" ]; then
|
|
if ! uci show firewall 2>/dev/null | grep -q "SimpleX-XFTP"; then
|
|
uci add firewall rule
|
|
uci set firewall.@rule[-1].name='SimpleX-XFTP'
|
|
uci set firewall.@rule[-1].src='wan'
|
|
uci set firewall.@rule[-1].dest_port="$XFTP_PORT"
|
|
uci set firewall.@rule[-1].proto='tcp'
|
|
uci set firewall.@rule[-1].target='ACCEPT'
|
|
uci set firewall.@rule[-1].enabled='1'
|
|
fi
|
|
fi
|
|
|
|
uci commit firewall
|
|
/etc/init.d/firewall reload 2>/dev/null
|
|
|
|
log "Firewall configured"
|
|
}
|
|
|
|
# ============================================================================
|
|
# HAProxy Integration
|
|
# ============================================================================
|
|
|
|
configure_haproxy() {
|
|
load_config
|
|
|
|
if [ ! -x /usr/sbin/haproxyctl ]; then
|
|
warn "HAProxy not installed, skipping integration"
|
|
return 0
|
|
fi
|
|
|
|
log "Configuring HAProxy..."
|
|
|
|
local domain="${TLS_DOMAIN:-simplex.local}"
|
|
|
|
# Check if vhost already exists
|
|
if uci show haproxy 2>/dev/null | grep -q "\.domain='$domain'"; then
|
|
warn "HAProxy vhost for $domain already exists"
|
|
return 0
|
|
fi
|
|
|
|
# Add SMP backend (TCP mode for TLS passthrough)
|
|
uci -q add haproxy backend
|
|
uci -q set haproxy.@backend[-1].name='simplex_smp'
|
|
uci -q set haproxy.@backend[-1].mode='tcp'
|
|
uci -q add_list haproxy.@backend[-1].server="simplex-smp 192.168.255.1:$SMP_PORT check"
|
|
|
|
uci commit haproxy
|
|
/etc/init.d/haproxy reload 2>/dev/null
|
|
|
|
log "HAProxy configured"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Status Display
|
|
# ============================================================================
|
|
|
|
show_status() {
|
|
load_config
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " SimpleX Chat Server Status v$VERSION"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
echo "Configuration:"
|
|
echo -e " Enabled: $([ "$ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
|
echo " Data Path: $DATA_PATH"
|
|
echo ""
|
|
|
|
# Container status
|
|
echo "Container:"
|
|
if container_exists; then
|
|
if container_running; then
|
|
echo -e " Status: ${GREEN}Running${NC}"
|
|
else
|
|
echo -e " Status: ${YELLOW}Stopped${NC}"
|
|
fi
|
|
else
|
|
echo -e " Status: ${RED}Not installed${NC}"
|
|
echo ""
|
|
echo "Run 'simplexctl install' to set up the server"
|
|
return
|
|
fi
|
|
echo ""
|
|
|
|
# SMP server status
|
|
echo "SMP Server:"
|
|
echo -e " Enabled: $([ "$SMP_ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
|
if [ "$SMP_ENABLED" = "1" ]; then
|
|
echo " Port: $SMP_PORT"
|
|
echo " Hostname: ${SMP_HOSTNAME:-<not set>}"
|
|
if pgrep smp-server >/dev/null 2>&1; then
|
|
echo -e " Status: ${GREEN}Running${NC}"
|
|
else
|
|
echo -e " Status: ${RED}Stopped${NC}"
|
|
fi
|
|
if [ -f "$SMP_DIR/fingerprint" ]; then
|
|
echo " Fingerprint: $(cat "$SMP_DIR/fingerprint" | head -c 20)..."
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# XFTP server status
|
|
echo "XFTP Server:"
|
|
echo -e " Enabled: $([ "$XFTP_ENABLED" = "1" ] && echo "${GREEN}Yes${NC}" || echo "${RED}No${NC}")"
|
|
if [ "$XFTP_ENABLED" = "1" ]; then
|
|
echo " Port: $XFTP_PORT"
|
|
echo " Hostname: ${XFTP_HOSTNAME:-<not set>}"
|
|
echo " Quota: $XFTP_STORAGE_QUOTA"
|
|
echo " File Expiry: $XFTP_FILE_EXPIRY"
|
|
if pgrep xftp-server >/dev/null 2>&1; then
|
|
echo -e " Status: ${GREEN}Running${NC}"
|
|
else
|
|
echo -e " Status: ${RED}Stopped${NC}"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Storage usage
|
|
if [ -d "$XFTP_DIR/files" ]; then
|
|
local storage_used=$(du -sh "$XFTP_DIR/files" 2>/dev/null | cut -f1)
|
|
echo "Storage:"
|
|
echo " Used: ${storage_used:-0}"
|
|
echo " Quota: $XFTP_STORAGE_QUOTA"
|
|
fi
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# Logs
|
|
# ============================================================================
|
|
|
|
show_logs() {
|
|
local service="${1:-all}"
|
|
local lines="${2:-50}"
|
|
|
|
case "$service" in
|
|
smp)
|
|
if [ -f "$SMP_DIR/smp-server-store.log" ]; then
|
|
tail -n "$lines" "$SMP_DIR/smp-server-store.log"
|
|
else
|
|
echo "No SMP logs found"
|
|
fi
|
|
;;
|
|
xftp)
|
|
if [ -f "$XFTP_DIR/xftp-server-store.log" ]; then
|
|
tail -n "$lines" "$XFTP_DIR/xftp-server-store.log"
|
|
else
|
|
echo "No XFTP logs found"
|
|
fi
|
|
;;
|
|
all)
|
|
echo "=== SMP Logs ==="
|
|
show_logs smp "$lines"
|
|
echo ""
|
|
echo "=== XFTP Logs ==="
|
|
show_logs xftp "$lines"
|
|
;;
|
|
*)
|
|
echo "Usage: simplexctl logs [smp|xftp|all] [lines]"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# Installation
|
|
# ============================================================================
|
|
|
|
install_simplex() {
|
|
log "Installing SimpleX Chat Server..."
|
|
|
|
# Check LXC
|
|
if ! command -v lxc-start >/dev/null 2>&1; then
|
|
error "LXC is required. Install with: opkg install lxc lxc-common"
|
|
return 1
|
|
fi
|
|
|
|
load_config
|
|
|
|
# Create directories
|
|
mkdir -p "$DATA_PATH" "$SMP_DIR" "$XFTP_DIR" "$CERT_PATH"
|
|
|
|
# Create LXC container
|
|
if ! container_exists; then
|
|
create_container || return 1
|
|
fi
|
|
|
|
# Start container
|
|
start_container || return 1
|
|
|
|
# Run setup inside container
|
|
lxc-attach -n "$CONTAINER_NAME" -- /tmp/setup.sh
|
|
|
|
# Download binaries
|
|
download_binaries || return 1
|
|
|
|
# Generate certificates
|
|
local hostname="${TLS_DOMAIN:-$(hostname)}"
|
|
if [ "$USE_LETSENCRYPT" = "1" ] && [ -n "$TLS_DOMAIN" ]; then
|
|
init_letsencrypt "$TLS_DOMAIN" "$TLS_EMAIL"
|
|
else
|
|
generate_self_signed_certs "$hostname"
|
|
fi
|
|
|
|
# Initialize servers
|
|
init_smp_server
|
|
init_xftp_server
|
|
|
|
# Configure firewall
|
|
configure_firewall
|
|
|
|
# Configure HAProxy if available
|
|
configure_haproxy
|
|
|
|
log ""
|
|
log "SimpleX Chat Server installed successfully!"
|
|
log ""
|
|
log "Next steps:"
|
|
log " 1. Set hostnames: uci set simplex.smp.hostname='smp.example.com'"
|
|
log " 2. Commit: uci commit simplex"
|
|
log " 3. Enable: uci set simplex.main.enabled=1 && uci commit simplex"
|
|
log " 4. Start: /etc/init.d/simplex start"
|
|
log ""
|
|
log "Get server addresses: simplexctl get-address"
|
|
log ""
|
|
}
|
|
|
|
uninstall_simplex() {
|
|
log "Uninstalling SimpleX Chat Server..."
|
|
|
|
# Stop services
|
|
/etc/init.d/simplex stop 2>/dev/null
|
|
|
|
# Stop and destroy container
|
|
if container_exists; then
|
|
stop_container
|
|
log "Removing container..."
|
|
lxc-destroy -n "$CONTAINER_NAME"
|
|
fi
|
|
|
|
# Remove firewall rules
|
|
local rules=$(uci show firewall 2>/dev/null | grep "SimpleX-" | cut -d'.' -f2 | cut -d'=' -f1 | sort -ru)
|
|
for rule in $rules; do
|
|
uci delete firewall.@rule[$rule]
|
|
done
|
|
uci commit firewall 2>/dev/null
|
|
|
|
warn "Data preserved at $DATA_PATH"
|
|
warn "Remove manually if desired: rm -rf $DATA_PATH"
|
|
|
|
log "Uninstall complete"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Backup / Restore
|
|
# ============================================================================
|
|
|
|
backup() {
|
|
local backup_file="${1:-/tmp/simplex-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
|
|
|
|
load_config
|
|
log "Creating backup..."
|
|
|
|
tar -czf "$backup_file" \
|
|
-C / \
|
|
etc/config/simplex \
|
|
"$SMP_DIR" \
|
|
"$XFTP_DIR" \
|
|
"$CERT_PATH" \
|
|
2>/dev/null
|
|
|
|
if [ -f "$backup_file" ]; then
|
|
local size=$(ls -lh "$backup_file" | awk '{print $5}')
|
|
log "Backup created: $backup_file ($size)"
|
|
else
|
|
error "Backup failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
restore() {
|
|
local backup_file="$1"
|
|
|
|
if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then
|
|
echo "Usage: simplexctl restore <backup_file>"
|
|
return 1
|
|
fi
|
|
|
|
log "Restoring from $backup_file..."
|
|
|
|
# Stop services
|
|
/etc/init.d/simplex stop 2>/dev/null
|
|
|
|
# Extract backup
|
|
tar -xzf "$backup_file" -C /
|
|
|
|
# Restart
|
|
/etc/init.d/simplex start
|
|
|
|
log "Restore complete"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Shell Access
|
|
# ============================================================================
|
|
|
|
container_shell() {
|
|
if ! container_running; then
|
|
start_container || return 1
|
|
fi
|
|
|
|
log "Entering container shell..."
|
|
lxc-attach -n "$CONTAINER_NAME"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Main
|
|
# ============================================================================
|
|
|
|
show_help() {
|
|
cat << EOF
|
|
SimpleX Chat Server Control v$VERSION
|
|
|
|
Usage: simplexctl <command> [options]
|
|
|
|
Installation:
|
|
install Install SimpleX servers in LXC container
|
|
uninstall Remove container (preserves data)
|
|
update Update SimpleX binaries to latest version
|
|
|
|
Service Control:
|
|
start Start SMP and XFTP servers
|
|
stop Stop servers
|
|
restart Restart servers
|
|
status Show service status
|
|
|
|
Server Addresses:
|
|
get-address Display server addresses for clients
|
|
|
|
Certificates:
|
|
init-certs Generate self-signed TLS certificates
|
|
init-letsencrypt Request Let's Encrypt certificates
|
|
|
|
Logs & Debug:
|
|
logs [smp|xftp] View server logs
|
|
shell Access container shell
|
|
|
|
Backup:
|
|
backup [file] Create configuration backup
|
|
restore <file> Restore from backup
|
|
|
|
Configuration:
|
|
configure-fw Configure firewall rules
|
|
configure-haproxy Add HAProxy integration
|
|
reload-config Reload configuration
|
|
|
|
Internal (used by init script):
|
|
service-run <svc> Run service in foreground
|
|
start-container Start LXC container
|
|
stop-container Stop LXC container
|
|
stop-services Stop server processes
|
|
|
|
Examples:
|
|
simplexctl install
|
|
simplexctl status
|
|
simplexctl get-address
|
|
simplexctl logs smp 100
|
|
|
|
EOF
|
|
}
|
|
|
|
case "$1" in
|
|
install)
|
|
install_simplex
|
|
;;
|
|
uninstall)
|
|
uninstall_simplex
|
|
;;
|
|
update)
|
|
load_config
|
|
download_binaries
|
|
;;
|
|
start)
|
|
start_container
|
|
/etc/init.d/simplex start
|
|
;;
|
|
stop)
|
|
/etc/init.d/simplex stop
|
|
;;
|
|
restart)
|
|
/etc/init.d/simplex restart
|
|
;;
|
|
status)
|
|
show_status
|
|
;;
|
|
get-address|get-addresses)
|
|
show_addresses
|
|
;;
|
|
init-certs)
|
|
load_config
|
|
generate_self_signed_certs "${2:-$TLS_DOMAIN}"
|
|
;;
|
|
init-letsencrypt)
|
|
load_config
|
|
init_letsencrypt "${2:-$TLS_DOMAIN}" "${3:-$TLS_EMAIL}"
|
|
;;
|
|
logs)
|
|
show_logs "$2" "$3"
|
|
;;
|
|
shell)
|
|
container_shell
|
|
;;
|
|
backup)
|
|
backup "$2"
|
|
;;
|
|
restore)
|
|
restore "$2"
|
|
;;
|
|
configure-fw)
|
|
configure_firewall
|
|
;;
|
|
configure-haproxy)
|
|
configure_haproxy
|
|
;;
|
|
reload-config)
|
|
reload_config
|
|
;;
|
|
service-run)
|
|
service_run "$2"
|
|
;;
|
|
start-container)
|
|
start_container
|
|
;;
|
|
stop-container)
|
|
stop_container
|
|
;;
|
|
stop-services)
|
|
stop_services
|
|
;;
|
|
-h|--help|help)
|
|
show_help
|
|
;;
|
|
*)
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
exit 0
|