mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 17:37:13 +00:00
Add comprehensive data migration covering all SecuBox services: Export modules (migration-export.sh): - dns: BIND zones, Vortex RPZ blocklists, Unbound, AdGuard, Pi-hole - databases: SQLite, MySQL/MariaDB, PostgreSQL, Redis dumps - scripts: Custom scripts, systemd units, rc.local, cron jobs - services: All /srv/* directories including: - Streamlit instances - Metablogizer/Metabolizer apps - Gitea/Git repositories with full history - Docker compose configurations - LXC container configs - mitmproxy, config-vault, saas-relay Import modules (migration-import.sh): - Corresponding restore functions for all new modules - Service detection and systemd integration - Database restore with owner/permission handling Enhanced HAProxy export: - conf.d modular architecture - Certificate management - Lua scripts and maps - mitmproxy route integration Total modules: 19 (network, firewall, wireguard, crowdsec, dhcp, haproxy, nginx, certs, content, vhosts, users, state, git, media, mail, accounts, dns, databases, scripts, services) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1256 lines
40 KiB
Bash
Executable File
1256 lines
40 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ══════════════════════════════════════════════════════════════════
|
|
# scripts/migration-import.sh — Import Migration Archive → SecuBox-DEB
|
|
#
|
|
# SecuBox-DEB :: Migration Data Saver
|
|
# CyberMind — https://cybermind.fr
|
|
# Author: Gérald Kerma <gandalf@gk2.net>
|
|
#
|
|
# Usage:
|
|
# bash scripts/migration-import.sh -f /tmp/migration.tar.gz --dry-run
|
|
# bash scripts/migration-import.sh -f /tmp/migration.tar.gz
|
|
# bash scripts/migration-import.sh -f /tmp/migration.tar.gz.enc --passphrase "secret"
|
|
# ══════════════════════════════════════════════════════════════════
|
|
set -euo pipefail
|
|
|
|
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
readonly REPO="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
readonly VERSION="2.1.0"
|
|
readonly TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
|
|
|
|
# ── Colors ──
|
|
CYAN='\033[0;36m'; GREEN='\033[0;32m'; GOLD='\033[0;33m'
|
|
RED='\033[0;31m'; PURPLE='\033[0;35m'; NC='\033[0m'
|
|
|
|
log() { echo -e "${CYAN}[import]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}[ OK ]${NC} $*"; }
|
|
warn() { echo -e "${GOLD}[ WARN ]${NC} $*"; }
|
|
err() { echo -e "${RED}[ FAIL ]${NC} $*" >&2; exit 1; }
|
|
section() { echo -e "\n${PURPLE}══════════════════════════════════════════════════════════════════${NC}"; echo -e "${PURPLE} $*${NC}"; echo -e "${PURPLE}══════════════════════════════════════════════════════════════════${NC}"; }
|
|
|
|
# ── Default values ──
|
|
ARCHIVE=""
|
|
PASSPHRASE=""
|
|
DRY_RUN=0
|
|
FORCE=0
|
|
SKIP_BACKUP=0
|
|
MODULES="all"
|
|
WORKDIR=""
|
|
TRANSFORM_DIR=""
|
|
|
|
# ── Target directories ──
|
|
SECUBOX_CONF="/etc/secubox"
|
|
NETPLAN_DIR="/etc/netplan"
|
|
NFTABLES_CONF="/etc/nftables.conf"
|
|
WIREGUARD_DIR="/etc/wireguard"
|
|
CROWDSEC_DIR="/etc/crowdsec"
|
|
HAPROXY_DIR="/etc/haproxy"
|
|
NGINX_DIR="/etc/nginx"
|
|
LETSENCRYPT_DIR="/etc/letsencrypt"
|
|
WWW_DIR="/srv/www"
|
|
VAR_LIB_SECUBOX="/var/lib/secubox"
|
|
ROLLBACK_DIR="/var/lib/secubox/rollback"
|
|
GIT_DIR="/srv/git"
|
|
MEDIA_DIR="/srv/media"
|
|
MAIL_DIR="/var/vmail"
|
|
POSTFIX_DIR="/etc/postfix"
|
|
DOVECOT_DIR="/etc/dovecot"
|
|
BIND_DIR="/etc/bind"
|
|
UNBOUND_DIR="/etc/unbound"
|
|
ADGUARD_DIR="/etc/adguardhome"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
SecuBox Migration Import — Version $VERSION
|
|
|
|
Import migration archive to SecuBox-DEB system.
|
|
|
|
Usage:
|
|
$(basename "$0") -f ARCHIVE [OPTIONS]
|
|
|
|
Required:
|
|
-f, --file ARCHIVE Migration archive (.tar.gz or .tar.gz.enc)
|
|
|
|
Options:
|
|
--dry-run Preview changes without applying
|
|
--force Apply without confirmation
|
|
--skip-backup Skip pre-import rollback snapshot (not recommended)
|
|
--passphrase PASS Decryption passphrase (prompts if encrypted)
|
|
-m, --modules LIST Comma-separated module list (default: all)
|
|
Available: network,firewall,wireguard,crowdsec,dhcp,
|
|
haproxy,nginx,certs,content,vhosts,users,state,
|
|
git,media,mail,accounts,dns,databases,scripts
|
|
--help Show this help
|
|
|
|
Examples:
|
|
# Preview import (dry-run)
|
|
$(basename "$0") -f /tmp/migration.tar.gz --dry-run
|
|
|
|
# Full import
|
|
$(basename "$0") -f /tmp/migration.tar.gz
|
|
|
|
# Import specific modules only
|
|
$(basename "$0") -f /tmp/migration.tar.gz -m wireguard,crowdsec
|
|
|
|
# Import encrypted archive
|
|
$(basename "$0") -f /tmp/migration.tar.gz.enc --passphrase "secret"
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# ── Parse arguments ──
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-f|--file) ARCHIVE="$2"; shift 2 ;;
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
--force) FORCE=1; shift ;;
|
|
--skip-backup) SKIP_BACKUP=1; shift ;;
|
|
--passphrase) PASSPHRASE="$2"; shift 2 ;;
|
|
-m|--modules) MODULES="$2"; shift 2 ;;
|
|
--help) usage ;;
|
|
*) err "Unknown option: $1" ;;
|
|
esac
|
|
done
|
|
|
|
# ── Validate required arguments ──
|
|
[[ -z "$ARCHIVE" ]] && err "Archive required. Use -f/--file"
|
|
[[ -f "$ARCHIVE" ]] || err "Archive not found: $ARCHIVE"
|
|
|
|
# ── Dry-run prefix ──
|
|
run() {
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
echo -e "${GOLD}[DRY-RUN]${NC} $*"
|
|
else
|
|
eval "$@"
|
|
fi
|
|
}
|
|
|
|
# ── Create work directory ──
|
|
WORKDIR=$(mktemp -d -t secubox-import-XXXXXX)
|
|
TRANSFORM_DIR=$(mktemp -d -t secubox-transform-XXXXXX)
|
|
trap 'rm -rf "$WORKDIR" "$TRANSFORM_DIR"' EXIT
|
|
|
|
# ── Extract archive ──
|
|
section "Extracting Migration Archive"
|
|
log "Archive: $ARCHIVE"
|
|
|
|
# Check if encrypted
|
|
if [[ "$ARCHIVE" == *.enc ]]; then
|
|
log "Archive is encrypted"
|
|
|
|
if [[ -z "$PASSPHRASE" ]]; then
|
|
read -sp "Enter decryption passphrase: " PASSPHRASE
|
|
echo
|
|
fi
|
|
|
|
DECRYPTED_ARCHIVE="${WORKDIR}/migration.tar.gz"
|
|
openssl enc -aes-256-cbc -d -pbkdf2 -in "$ARCHIVE" -out "$DECRYPTED_ARCHIVE" -pass pass:"$PASSPHRASE" || err "Decryption failed"
|
|
ARCHIVE="$DECRYPTED_ARCHIVE"
|
|
ok "Archive decrypted"
|
|
fi
|
|
|
|
# Extract
|
|
tar -xzf "$ARCHIVE" -C "$WORKDIR"
|
|
ok "Archive extracted"
|
|
|
|
# ── Validate manifest ──
|
|
section "Validating Migration Archive"
|
|
MANIFEST="$WORKDIR/manifest.json"
|
|
[[ -f "$MANIFEST" ]] || err "Invalid archive: manifest.json not found"
|
|
|
|
# Parse manifest (basic parsing without jq)
|
|
SOURCE_HOST=$(grep -o '"host": "[^"]*"' "$MANIFEST" | cut -d'"' -f4 || echo "unknown")
|
|
SOURCE_TYPE=$(grep -o '"type": "[^"]*"' "$MANIFEST" | cut -d'"' -f4 || echo "unknown")
|
|
ARCHIVE_VERSION=$(grep -o '"version": "[^"]*"' "$MANIFEST" | head -1 | cut -d'"' -f4 || echo "unknown")
|
|
|
|
log "Source: $SOURCE_HOST ($SOURCE_TYPE)"
|
|
log "Archive version: $ARCHIVE_VERSION"
|
|
|
|
# Verify checksums
|
|
if [[ -f "$WORKDIR/checksums.sha256" ]]; then
|
|
log "Verifying checksums..."
|
|
(cd "$WORKDIR" && sha256sum -c checksums.sha256 --quiet 2>/dev/null) || warn "Some checksums failed"
|
|
ok "Checksums verified"
|
|
fi
|
|
|
|
# ── Transform configs ──
|
|
section "Transforming UCI Configs"
|
|
python3 "$SCRIPT_DIR/migration-transform.py" transform-all "$WORKDIR" "$TRANSFORM_DIR"
|
|
ok "Configs transformed"
|
|
|
|
# ── Create rollback snapshot ──
|
|
if [[ $SKIP_BACKUP -eq 0 && $DRY_RUN -eq 0 ]]; then
|
|
section "Creating Pre-Import Rollback Snapshot"
|
|
|
|
ROLLBACK_SNAPSHOT="${ROLLBACK_DIR}/pre-migration-${TIMESTAMP}"
|
|
mkdir -p "$ROLLBACK_SNAPSHOT"
|
|
|
|
# Backup current configs
|
|
[[ -d "$SECUBOX_CONF" ]] && cp -a "$SECUBOX_CONF" "$ROLLBACK_SNAPSHOT/" || true
|
|
[[ -d "$NETPLAN_DIR" ]] && cp -a "$NETPLAN_DIR" "$ROLLBACK_SNAPSHOT/" || true
|
|
[[ -f "$NFTABLES_CONF" ]] && cp -a "$NFTABLES_CONF" "$ROLLBACK_SNAPSHOT/" || true
|
|
[[ -d "$WIREGUARD_DIR" ]] && cp -a "$WIREGUARD_DIR" "$ROLLBACK_SNAPSHOT/" || true
|
|
[[ -d "$CROWDSEC_DIR" ]] && cp -a "$CROWDSEC_DIR" "$ROLLBACK_SNAPSHOT/" || true
|
|
|
|
ok "Rollback snapshot created: $ROLLBACK_SNAPSHOT"
|
|
fi
|
|
|
|
# ── Confirmation ──
|
|
if [[ $DRY_RUN -eq 0 && $FORCE -eq 0 ]]; then
|
|
section "Confirmation Required"
|
|
echo -e "${GOLD}The following changes will be applied to this system:${NC}"
|
|
echo " - Network: netplan config"
|
|
echo " - Firewall: nftables rules"
|
|
echo " - Services: WireGuard, CrowdSec, HAProxy, Nginx"
|
|
echo " - Content: Web files, certificates"
|
|
echo
|
|
read -p "Continue? [y/N] " confirm
|
|
[[ "$confirm" =~ ^[Yy]$ ]] || { log "Aborted."; exit 0; }
|
|
fi
|
|
|
|
# ── Module import functions ──
|
|
|
|
import_network() {
|
|
section "Importing: Network Configuration"
|
|
|
|
local src="$TRANSFORM_DIR/netplan/00-secubox.yaml"
|
|
if [[ -f "$src" ]]; then
|
|
run "mkdir -p '$NETPLAN_DIR'"
|
|
run "cp '$src' '$NETPLAN_DIR/00-secubox.yaml'"
|
|
run "chmod 600 '$NETPLAN_DIR/00-secubox.yaml'"
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
log "Applying netplan..."
|
|
netplan apply 2>/dev/null || warn "netplan apply failed (may need reboot)"
|
|
fi
|
|
|
|
ok "Network config imported"
|
|
else
|
|
warn "No network config to import"
|
|
fi
|
|
}
|
|
|
|
import_firewall() {
|
|
section "Importing: Firewall Rules"
|
|
|
|
local src="$TRANSFORM_DIR/nftables.conf"
|
|
if [[ -f "$src" ]]; then
|
|
run "cp '$src' '$NFTABLES_CONF'"
|
|
run "chmod 644 '$NFTABLES_CONF'"
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
log "Applying nftables rules..."
|
|
nft -f "$NFTABLES_CONF" 2>/dev/null || warn "nftables apply failed"
|
|
systemctl enable nftables 2>/dev/null || true
|
|
fi
|
|
|
|
ok "Firewall rules imported"
|
|
else
|
|
warn "No firewall config to import"
|
|
fi
|
|
}
|
|
|
|
import_wireguard() {
|
|
section "Importing: WireGuard Configuration"
|
|
|
|
local secrets_src="$WORKDIR/secrets/wireguard"
|
|
if [[ -d "$secrets_src" && -n "$(ls -A "$secrets_src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$WIREGUARD_DIR'"
|
|
|
|
for conf in "$secrets_src"/*.conf; do
|
|
[[ -f "$conf" ]] || continue
|
|
local name=$(basename "$conf")
|
|
run "cp '$conf' '$WIREGUARD_DIR/$name'"
|
|
run "chmod 600 '$WIREGUARD_DIR/$name'"
|
|
log " Imported: $name"
|
|
|
|
# Enable interface
|
|
local iface="${name%.conf}"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl enable "wg-quick@${iface}" 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
ok "WireGuard configs imported"
|
|
else
|
|
warn "No WireGuard configs to import"
|
|
fi
|
|
}
|
|
|
|
import_crowdsec() {
|
|
section "Importing: CrowdSec Configuration"
|
|
|
|
local src="$WORKDIR/configs/crowdsec"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$CROWDSEC_DIR'"
|
|
|
|
# Main config
|
|
[[ -f "$src/config.yaml" ]] && run "cp '$src/config.yaml' '$CROWDSEC_DIR/'"
|
|
|
|
# Acquis configs
|
|
if [[ -d "$src/acquis.d" ]]; then
|
|
run "mkdir -p '$CROWDSEC_DIR/acquis.d'"
|
|
run "cp -r '$src/acquis.d/'* '$CROWDSEC_DIR/acquis.d/' 2>/dev/null || true"
|
|
fi
|
|
|
|
# Local parsers/scenarios
|
|
for subdir in parsers scenarios postoverflows; do
|
|
if [[ -d "$src/$subdir" ]]; then
|
|
run "mkdir -p '$CROWDSEC_DIR/$subdir'"
|
|
run "cp -r '$src/$subdir/'* '$CROWDSEC_DIR/$subdir/' 2>/dev/null || true"
|
|
fi
|
|
done
|
|
|
|
# API credentials from secrets
|
|
local creds="$WORKDIR/secrets/crowdsec/local_api_credentials.yaml"
|
|
if [[ -f "$creds" ]]; then
|
|
run "cp '$creds' '$CROWDSEC_DIR/'"
|
|
run "chmod 600 '$CROWDSEC_DIR/local_api_credentials.yaml'"
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart crowdsec 2>/dev/null || warn "CrowdSec restart failed"
|
|
fi
|
|
|
|
ok "CrowdSec configs imported"
|
|
else
|
|
warn "No CrowdSec configs to import"
|
|
fi
|
|
}
|
|
|
|
import_dhcp() {
|
|
section "Importing: DHCP/DNS Configuration"
|
|
|
|
local src="$TRANSFORM_DIR/dnsmasq.conf"
|
|
if [[ -f "$src" ]]; then
|
|
run "cp '$src' '/etc/dnsmasq.d/secubox.conf'"
|
|
run "chmod 644 '/etc/dnsmasq.d/secubox.conf'"
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart dnsmasq 2>/dev/null || warn "dnsmasq restart failed"
|
|
fi
|
|
|
|
ok "DHCP/DNS config imported"
|
|
else
|
|
warn "No DHCP config to import"
|
|
fi
|
|
}
|
|
|
|
import_haproxy() {
|
|
section "Importing: HAProxy Configuration"
|
|
|
|
local src="$WORKDIR/configs/haproxy"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$HAPROXY_DIR'"
|
|
|
|
# Main config
|
|
[[ -f "$src/haproxy.cfg" ]] && run "cp '$src/haproxy.cfg' '$HAPROXY_DIR/'"
|
|
|
|
# Additional configs
|
|
if [[ -d "$src/conf.d" ]]; then
|
|
run "mkdir -p '$HAPROXY_DIR/conf.d'"
|
|
run "cp -r '$src/conf.d/'* '$HAPROXY_DIR/conf.d/' 2>/dev/null || true"
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
# Validate config first
|
|
haproxy -c -f "$HAPROXY_DIR/haproxy.cfg" 2>/dev/null && \
|
|
systemctl restart haproxy 2>/dev/null || warn "HAProxy restart failed"
|
|
fi
|
|
|
|
ok "HAProxy configs imported"
|
|
else
|
|
warn "No HAProxy configs to import"
|
|
fi
|
|
}
|
|
|
|
import_nginx() {
|
|
section "Importing: Nginx Configuration"
|
|
|
|
local src="$WORKDIR/configs/nginx"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# Sites
|
|
if [[ -d "$src/sites-available" ]]; then
|
|
run "mkdir -p '$NGINX_DIR/sites-available'"
|
|
for site in "$src/sites-available"/*; do
|
|
[[ -f "$site" ]] || continue
|
|
local name=$(basename "$site")
|
|
run "cp '$site' '$NGINX_DIR/sites-available/$name'"
|
|
log " Imported site: $name"
|
|
done
|
|
fi
|
|
|
|
if [[ -d "$src/sites-enabled" ]]; then
|
|
run "mkdir -p '$NGINX_DIR/sites-enabled'"
|
|
for site in "$src/sites-enabled"/*; do
|
|
[[ -f "$site" ]] || continue
|
|
local name=$(basename "$site")
|
|
# Create symlink if available site exists
|
|
if [[ -f "$NGINX_DIR/sites-available/$name" ]]; then
|
|
run "ln -sf '../sites-available/$name' '$NGINX_DIR/sites-enabled/$name'"
|
|
else
|
|
run "cp '$site' '$NGINX_DIR/sites-enabled/$name'"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
nginx -t 2>/dev/null && systemctl reload nginx 2>/dev/null || warn "Nginx reload failed"
|
|
fi
|
|
|
|
ok "Nginx configs imported"
|
|
else
|
|
warn "No Nginx configs to import"
|
|
fi
|
|
}
|
|
|
|
import_certs() {
|
|
section "Importing: SSL Certificates"
|
|
|
|
local src="$WORKDIR/secrets/certs"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# Let's Encrypt
|
|
if [[ -d "$src/letsencrypt" ]]; then
|
|
run "mkdir -p '$LETSENCRYPT_DIR'"
|
|
run "cp -r '$src/letsencrypt/'* '$LETSENCRYPT_DIR/' 2>/dev/null || true"
|
|
run "chmod -R 600 '$LETSENCRYPT_DIR/'"
|
|
log " Imported: Let's Encrypt certificates"
|
|
fi
|
|
|
|
# Custom certs
|
|
local key_count=0
|
|
for key in "$src"/*.key; do
|
|
[[ -f "$key" ]] || continue
|
|
run "mkdir -p '/etc/ssl/private'"
|
|
run "cp '$key' '/etc/ssl/private/'"
|
|
run "chmod 600 '/etc/ssl/private/$(basename "$key")'"
|
|
((key_count++))
|
|
done
|
|
[[ $key_count -gt 0 ]] && log " Imported: $key_count private keys"
|
|
|
|
local cert_count=0
|
|
for cert in "$src"/*.pem "$src"/*.crt; do
|
|
[[ -f "$cert" ]] || continue
|
|
run "mkdir -p '/etc/ssl/certs'"
|
|
run "cp '$cert' '/etc/ssl/certs/'"
|
|
((cert_count++))
|
|
done
|
|
[[ $cert_count -gt 0 ]] && log " Imported: $cert_count certificates"
|
|
|
|
ok "Certificates imported"
|
|
else
|
|
warn "No certificates to import"
|
|
fi
|
|
}
|
|
|
|
import_content() {
|
|
section "Importing: Web Content"
|
|
|
|
local src="$WORKDIR/content"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$WWW_DIR'"
|
|
run "cp -r '$src/'* '$WWW_DIR/' 2>/dev/null || true"
|
|
|
|
# Fix ownership
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R www-data:www-data "$WWW_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1)
|
|
ok "Web content imported ($size)"
|
|
else
|
|
warn "No web content to import"
|
|
fi
|
|
}
|
|
|
|
import_vhosts() {
|
|
section "Importing: Virtual Hosts"
|
|
|
|
local src="$TRANSFORM_DIR/vhosts"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$SECUBOX_CONF/vhosts'"
|
|
|
|
for vhost in "$src"/*; do
|
|
[[ -f "$vhost" ]] || continue
|
|
local name=$(basename "$vhost")
|
|
run "cp '$vhost' '$SECUBOX_CONF/vhosts/$name'"
|
|
log " Imported: $name"
|
|
done
|
|
|
|
ok "Virtual hosts imported"
|
|
else
|
|
warn "No virtual hosts to import"
|
|
fi
|
|
}
|
|
|
|
import_users() {
|
|
section "Importing: User Accounts"
|
|
|
|
local src="$WORKDIR/configs/users"
|
|
local secrets_src="$WORKDIR/secrets/users"
|
|
|
|
# Import SecuBox auth.toml
|
|
if [[ -f "$src/auth.toml" ]]; then
|
|
run "mkdir -p '$SECUBOX_CONF'"
|
|
run "cp '$src/auth.toml' '$SECUBOX_CONF/auth.toml'"
|
|
run "chmod 600 '$SECUBOX_CONF/auth.toml'"
|
|
ok "SecuBox auth config imported"
|
|
fi
|
|
|
|
# Import SSH authorized keys
|
|
if [[ -f "$secrets_src/authorized_keys" ]]; then
|
|
run "mkdir -p '/root/.ssh'"
|
|
# Append rather than replace
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
cat "$secrets_src/authorized_keys" >> /root/.ssh/authorized_keys
|
|
sort -u /root/.ssh/authorized_keys -o /root/.ssh/authorized_keys
|
|
chmod 600 /root/.ssh/authorized_keys
|
|
else
|
|
log "Would append SSH keys to /root/.ssh/authorized_keys"
|
|
fi
|
|
ok "SSH authorized keys imported"
|
|
fi
|
|
|
|
# Note: We don't import system users (/etc/passwd, /etc/shadow)
|
|
# Those should be created by package installation
|
|
warn "System users not imported (use package installation)"
|
|
}
|
|
|
|
import_state() {
|
|
section "Importing: Service State Data"
|
|
|
|
local src="$WORKDIR/state"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# CrowdSec decisions
|
|
if [[ -f "$src/crowdsec/crowdsec.db" ]]; then
|
|
run "mkdir -p '/var/lib/crowdsec/data'"
|
|
run "cp '$src/crowdsec/crowdsec.db' '/var/lib/crowdsec/data/'"
|
|
run "chown crowdsec:crowdsec '/var/lib/crowdsec/data/crowdsec.db' 2>/dev/null || true"
|
|
log " Imported: CrowdSec decisions database"
|
|
fi
|
|
|
|
# SecuBox state
|
|
if [[ -d "$src/secubox" && -n "$(ls -A "$src/secubox" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$VAR_LIB_SECUBOX'"
|
|
run "cp -r '$src/secubox/'* '$VAR_LIB_SECUBOX/' 2>/dev/null || true"
|
|
log " Imported: SecuBox state data"
|
|
fi
|
|
|
|
ok "State data imported"
|
|
else
|
|
warn "No state data to import"
|
|
fi
|
|
}
|
|
|
|
import_git() {
|
|
section "Importing: Git Repositories"
|
|
|
|
local src="$WORKDIR/content/git"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$GIT_DIR'"
|
|
|
|
local repo_count=0
|
|
|
|
# Import bare repos
|
|
for repo_tar in "$src"/*.git.tar.gz; do
|
|
[[ -f "$repo_tar" ]] || continue
|
|
local repo_name=$(basename "$repo_tar" .tar.gz)
|
|
log " Importing bare repo: $repo_name"
|
|
run "tar -xzf '$repo_tar' -C '$GIT_DIR/' --strip-components=2 2>/dev/null || true"
|
|
((repo_count++))
|
|
done
|
|
|
|
# Import full repos (with working trees)
|
|
for repo_tar in "$src"/*-full.tar.gz; do
|
|
[[ -f "$repo_tar" ]] || continue
|
|
local repo_name=$(basename "$repo_tar" -full.tar.gz)
|
|
log " Importing repo with worktree: $repo_name"
|
|
run "mkdir -p '$GIT_DIR/$repo_name'"
|
|
run "tar -xzf '$repo_tar' -C '$GIT_DIR/$repo_name' --strip-components=2 2>/dev/null || true"
|
|
((repo_count++))
|
|
done
|
|
|
|
# Import Gitea/Gogs/GitLab repos
|
|
for git_app in gitea gogs gitlab; do
|
|
if [[ -d "$src/$git_app" ]]; then
|
|
log " Importing $git_app repositories..."
|
|
run "mkdir -p '/var/lib/$git_app/repositories'"
|
|
run "cp -r '$src/$git_app/'* '/var/lib/$git_app/repositories/' 2>/dev/null || true"
|
|
|
|
# Import config if present
|
|
if [[ -f "$src/$git_app/app.ini" ]]; then
|
|
run "mkdir -p '/etc/$git_app'"
|
|
run "cp '$src/$git_app/app.ini' '/etc/$git_app/'"
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart "$git_app" 2>/dev/null || warn "$git_app restart failed"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Fix ownership
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R git:git "$GIT_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "Git repositories imported ($repo_count repos, $size)"
|
|
else
|
|
warn "No git repositories to import"
|
|
fi
|
|
}
|
|
|
|
import_media() {
|
|
section "Importing: Media Files"
|
|
|
|
local src="$WORKDIR/content/media"
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
run "mkdir -p '$MEDIA_DIR'"
|
|
|
|
# Import general media directories
|
|
for media_sub in "$src"/srv_* "$src"/var_*; do
|
|
[[ -d "$media_sub" ]] || continue
|
|
local sub_name=$(basename "$media_sub")
|
|
log " Importing media: $sub_name"
|
|
run "cp -r '$media_sub/'* '$MEDIA_DIR/' 2>/dev/null || true"
|
|
done
|
|
|
|
# Import PeerTube videos
|
|
if [[ -d "$src/peertube" ]]; then
|
|
log " Importing PeerTube videos..."
|
|
run "mkdir -p '/var/www/peertube/storage/videos'"
|
|
run "cp -r '$src/peertube/'* '/var/www/peertube/storage/videos/' 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R peertube:peertube /var/www/peertube/storage 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Import Jellyfin library
|
|
if [[ -d "$src/jellyfin" ]]; then
|
|
log " Importing Jellyfin library..."
|
|
run "mkdir -p '/var/lib/jellyfin/data/library'"
|
|
run "cp -r '$src/jellyfin/'* '/var/lib/jellyfin/data/library/' 2>/dev/null || true"
|
|
if [[ -f "$src/jellyfin/system.xml" ]]; then
|
|
run "mkdir -p '/etc/jellyfin'"
|
|
run "cp '$src/jellyfin/system.xml' '/etc/jellyfin/'"
|
|
fi
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R jellyfin:jellyfin /var/lib/jellyfin 2>/dev/null || true
|
|
systemctl restart jellyfin 2>/dev/null || warn "Jellyfin restart failed"
|
|
fi
|
|
fi
|
|
|
|
# Import Nextcloud user files
|
|
if [[ -d "$src/nextcloud" ]]; then
|
|
log " Importing Nextcloud files..."
|
|
run "mkdir -p '/var/www/nextcloud/data'"
|
|
run "cp -r '$src/nextcloud/'* '/var/www/nextcloud/data/' 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R www-data:www-data /var/www/nextcloud/data 2>/dev/null || true
|
|
# Run Nextcloud file scan
|
|
sudo -u www-data php /var/www/nextcloud/occ files:scan --all 2>/dev/null || warn "Nextcloud scan failed"
|
|
fi
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "Media files imported ($size)"
|
|
else
|
|
warn "No media files to import"
|
|
fi
|
|
}
|
|
|
|
import_mail() {
|
|
section "Importing: Email Data"
|
|
|
|
local src="$WORKDIR/content/mail"
|
|
local secrets_src="$WORKDIR/secrets/mail"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# Import Maildir
|
|
for mail_dir in "$src"/var_* "$src"/home_*; do
|
|
[[ -d "$mail_dir" ]] || continue
|
|
local dir_name=$(basename "$mail_dir")
|
|
log " Importing maildir: $dir_name"
|
|
|
|
if [[ "$dir_name" == "var_vmail" || "$dir_name" == "var_mail" ]]; then
|
|
run "mkdir -p '$MAIL_DIR'"
|
|
run "cp -r '$mail_dir/'* '$MAIL_DIR/' 2>/dev/null || true"
|
|
fi
|
|
done
|
|
|
|
# Import Postfix config
|
|
if [[ -d "$src/postfix" ]]; then
|
|
log " Importing Postfix configuration..."
|
|
run "mkdir -p '$POSTFIX_DIR'"
|
|
run "cp -r '$src/postfix/'* '$POSTFIX_DIR/' 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
postmap /etc/postfix/virtual 2>/dev/null || true
|
|
postmap /etc/postfix/vmailbox 2>/dev/null || true
|
|
systemctl restart postfix 2>/dev/null || warn "Postfix restart failed"
|
|
fi
|
|
fi
|
|
|
|
# Import Dovecot config
|
|
if [[ -d "$src/dovecot" ]]; then
|
|
log " Importing Dovecot configuration..."
|
|
run "mkdir -p '$DOVECOT_DIR'"
|
|
run "cp -r '$src/dovecot/'* '$DOVECOT_DIR/' 2>/dev/null || true"
|
|
fi
|
|
|
|
# Import DKIM keys from secrets
|
|
if [[ -d "$secrets_src/opendkim" ]]; then
|
|
log " Importing DKIM keys..."
|
|
run "mkdir -p '/etc/opendkim'"
|
|
run "cp -r '$secrets_src/opendkim/'* '/etc/opendkim/' 2>/dev/null || true"
|
|
run "chmod 600 /etc/opendkim/keys/*/*.private 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R opendkim:opendkim /etc/opendkim 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Import mail aliases
|
|
if [[ -f "$src/aliases" ]]; then
|
|
run "cp '$src/aliases' '/etc/aliases'"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
newaliases 2>/dev/null || warn "newaliases failed"
|
|
fi
|
|
fi
|
|
|
|
if [[ -f "$src/mailname" ]]; then
|
|
run "cp '$src/mailname' '/etc/mailname'"
|
|
fi
|
|
|
|
# Import virtual mailbox config
|
|
if [[ -f "$src/vmailbox" ]]; then
|
|
run "cp '$src/vmailbox' '/etc/postfix/vmailbox'"
|
|
fi
|
|
|
|
# Import Dovecot users from secrets
|
|
if [[ -f "$secrets_src/dovecot-users" ]]; then
|
|
run "mkdir -p '/etc/dovecot'"
|
|
run "cp '$secrets_src/dovecot-users' '/etc/dovecot/users'"
|
|
run "chmod 600 '/etc/dovecot/users'"
|
|
fi
|
|
|
|
# Restart mail services
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart dovecot 2>/dev/null || warn "Dovecot restart failed"
|
|
systemctl restart opendkim 2>/dev/null || true
|
|
fi
|
|
|
|
# Fix maildir ownership
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R vmail:vmail "$MAIL_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "Email data imported ($size)"
|
|
else
|
|
warn "No email data to import"
|
|
fi
|
|
}
|
|
|
|
import_accounts() {
|
|
section "Importing: User Accounts"
|
|
|
|
local src="$WORKDIR/content/accounts"
|
|
local secrets_src="$WORKDIR/secrets/accounts"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
local user_count=0
|
|
|
|
# Create users from passwd/shadow (interactive - requires confirmation)
|
|
if [[ -f "$secrets_src/passwd" && -f "$secrets_src/shadow" ]]; then
|
|
log " Importing user accounts..."
|
|
|
|
while IFS=: read -r username x uid gid gecos home shell; do
|
|
[[ -z "$username" ]] && continue
|
|
|
|
# Check if user exists
|
|
if ! id "$username" &>/dev/null; then
|
|
log " Creating user: $username (UID: $uid)"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
# Create user with same UID/GID
|
|
useradd -m -u "$uid" -s "$shell" -c "$gecos" "$username" 2>/dev/null || \
|
|
useradd -m -s "$shell" -c "$gecos" "$username" 2>/dev/null || \
|
|
warn "Failed to create user: $username"
|
|
|
|
# Set password from shadow
|
|
local shadow_entry=$(grep "^${username}:" "$secrets_src/shadow" 2>/dev/null)
|
|
if [[ -n "$shadow_entry" ]]; then
|
|
echo "$shadow_entry" | chpasswd -e 2>/dev/null || true
|
|
fi
|
|
fi
|
|
((user_count++))
|
|
else
|
|
log " User exists: $username (skipping)"
|
|
fi
|
|
done < "$secrets_src/passwd"
|
|
fi
|
|
|
|
# Import home directories
|
|
if [[ -d "$src/homes" ]]; then
|
|
for user_home in "$src/homes"/*; do
|
|
[[ -d "$user_home" ]] || continue
|
|
local username=$(basename "$user_home")
|
|
local target_home="/home/$username"
|
|
|
|
# Get actual home from passwd if user exists
|
|
if id "$username" &>/dev/null; then
|
|
target_home=$(getent passwd "$username" | cut -d: -f6)
|
|
fi
|
|
|
|
log " Importing home directory: $username → $target_home"
|
|
run "mkdir -p '$target_home'"
|
|
run "cp -r '$user_home/'* '$target_home/' 2>/dev/null || true"
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R "$username:$username" "$target_home" 2>/dev/null || true
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Import group memberships
|
|
if [[ -f "$secrets_src/group" ]]; then
|
|
log " Importing group memberships..."
|
|
while IFS=: read -r groupname x gid members; do
|
|
[[ -z "$groupname" ]] && continue
|
|
[[ -z "$members" ]] && continue
|
|
|
|
# Create group if it doesn't exist
|
|
if ! getent group "$groupname" &>/dev/null; then
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
groupadd -g "$gid" "$groupname" 2>/dev/null || \
|
|
groupadd "$groupname" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Add members to group
|
|
IFS=',' read -ra MEMBERS <<< "$members"
|
|
for member in "${MEMBERS[@]}"; do
|
|
if id "$member" &>/dev/null; then
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
usermod -aG "$groupname" "$member" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done
|
|
done < "$secrets_src/group"
|
|
fi
|
|
|
|
# Import sudoers
|
|
if [[ -f "$src/sudoers.d" && -s "$src/sudoers.d" ]]; then
|
|
log " Importing sudoers rules..."
|
|
run "cp '$src/sudoers.d' '/etc/sudoers.d/migrated'"
|
|
run "chmod 440 '/etc/sudoers.d/migrated'"
|
|
# Validate sudoers
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
visudo -c -f /etc/sudoers.d/migrated 2>/dev/null || {
|
|
warn "Invalid sudoers rules - removing"
|
|
rm -f /etc/sudoers.d/migrated
|
|
}
|
|
fi
|
|
fi
|
|
|
|
# Import crontabs
|
|
if [[ -d "$src/crontabs" ]]; then
|
|
log " Importing user crontabs..."
|
|
for crontab_file in "$src/crontabs"/*; do
|
|
[[ -f "$crontab_file" ]] || continue
|
|
local cron_user=$(basename "$crontab_file")
|
|
if id "$cron_user" &>/dev/null; then
|
|
run "mkdir -p '/var/spool/cron/crontabs'"
|
|
run "cp '$crontab_file' '/var/spool/cron/crontabs/$cron_user'"
|
|
run "chmod 600 '/var/spool/cron/crontabs/$cron_user'"
|
|
run "chown '$cron_user:crontab' '/var/spool/cron/crontabs/$cron_user' 2>/dev/null || true"
|
|
log " Imported crontab: $cron_user"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Import system cron jobs
|
|
for cron_dir in cron.d cron.daily cron.weekly cron.monthly; do
|
|
if [[ -d "$src/$cron_dir" ]]; then
|
|
run "cp -r '$src/$cron_dir/'* '/etc/$cron_dir/' 2>/dev/null || true"
|
|
fi
|
|
done
|
|
|
|
ok "User accounts imported ($user_count new users)"
|
|
else
|
|
warn "No user accounts to import"
|
|
fi
|
|
}
|
|
|
|
import_dns() {
|
|
section "Importing: DNS Services"
|
|
|
|
local src="$WORKDIR/configs/dns"
|
|
local secrets_src="$WORKDIR/secrets/dns"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# BIND/named
|
|
if [[ -d "$src/bind" ]]; then
|
|
log " Importing BIND configuration..."
|
|
run "mkdir -p '$BIND_DIR'"
|
|
run "cp -r '$src/bind/'* '$BIND_DIR/' 2>/dev/null || true"
|
|
|
|
# Import zones
|
|
if [[ -d "$src/bind/zones" ]]; then
|
|
run "mkdir -p '$BIND_DIR/zones'"
|
|
run "cp -r '$src/bind/zones/'* '$BIND_DIR/zones/' 2>/dev/null || true"
|
|
local zone_count=$(ls "$src/bind/zones"/*.zone 2>/dev/null | wc -l || echo "0")
|
|
log " Imported $zone_count zone files"
|
|
fi
|
|
|
|
# Import RNDC key from secrets
|
|
if [[ -f "$secrets_src/rndc.key" ]]; then
|
|
run "cp '$secrets_src/rndc.key' '$BIND_DIR/'"
|
|
run "chmod 600 '$BIND_DIR/rndc.key'"
|
|
fi
|
|
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R bind:bind "$BIND_DIR" 2>/dev/null || true
|
|
systemctl restart named 2>/dev/null || systemctl restart bind9 2>/dev/null || warn "BIND restart failed"
|
|
fi
|
|
fi
|
|
|
|
# Unbound
|
|
if [[ -d "$src/unbound" ]]; then
|
|
log " Importing Unbound configuration..."
|
|
run "mkdir -p '$UNBOUND_DIR'"
|
|
run "cp -r '$src/unbound/'* '$UNBOUND_DIR/' 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart unbound 2>/dev/null || warn "Unbound restart failed"
|
|
fi
|
|
fi
|
|
|
|
# Vortex DNS (dnsmasq blocklists)
|
|
if [[ -d "$src/vortex" ]]; then
|
|
log " Importing Vortex DNS blocklists..."
|
|
run "mkdir -p '/etc/dnsmasq.d/vortex'"
|
|
run "cp -r '$src/vortex/'* '/etc/dnsmasq.d/vortex/' 2>/dev/null || true"
|
|
fi
|
|
|
|
# AdGuard Home
|
|
if [[ -d "$src/adguardhome" ]]; then
|
|
log " Importing AdGuard Home configuration..."
|
|
run "mkdir -p '$ADGUARD_DIR'"
|
|
run "cp '$src/adguardhome/AdGuardHome.yaml' '$ADGUARD_DIR/' 2>/dev/null || true"
|
|
if [[ -d "$src/adguardhome/data" ]]; then
|
|
run "mkdir -p '/var/lib/adguardhome/data'"
|
|
run "cp -r '$src/adguardhome/data/'* '/var/lib/adguardhome/data/' 2>/dev/null || true"
|
|
fi
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl restart AdGuardHome 2>/dev/null || warn "AdGuard Home restart failed"
|
|
fi
|
|
fi
|
|
|
|
# Pi-hole
|
|
if [[ -d "$src/pihole" ]]; then
|
|
log " Importing Pi-hole configuration..."
|
|
run "mkdir -p '/etc/pihole'"
|
|
run "cp -r '$src/pihole/'* '/etc/pihole/' 2>/dev/null || true"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
pihole restartdns 2>/dev/null || warn "Pi-hole restart failed"
|
|
fi
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "DNS services imported ($size)"
|
|
else
|
|
warn "No DNS configuration to import"
|
|
fi
|
|
}
|
|
|
|
import_databases() {
|
|
section "Importing: Databases"
|
|
|
|
local src="$WORKDIR/state/databases"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# SQLite databases - restore to original locations
|
|
for db_dir in "$src"/var_lib_*; do
|
|
[[ -d "$db_dir" ]] || continue
|
|
local original_path=$(echo "$db_dir" | sed 's/_/\//g' | sed 's/.*var/\/var/')
|
|
log " Restoring databases to: $original_path"
|
|
run "mkdir -p '$original_path'"
|
|
run "cp -r '$db_dir/'* '$original_path/' 2>/dev/null || true"
|
|
done
|
|
|
|
# MySQL restore
|
|
if [[ -f "$src/mysql/all-databases.sql" ]]; then
|
|
log " Restoring MySQL databases..."
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
mysql < "$src/mysql/all-databases.sql" 2>/dev/null || warn "MySQL restore failed"
|
|
else
|
|
log " Would restore MySQL from all-databases.sql"
|
|
fi
|
|
fi
|
|
|
|
# PostgreSQL restore
|
|
if [[ -f "$src/postgresql/all-databases.sql" ]]; then
|
|
log " Restoring PostgreSQL databases..."
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
su - postgres -c "psql < $src/postgresql/all-databases.sql" 2>/dev/null || warn "PostgreSQL restore failed"
|
|
else
|
|
log " Would restore PostgreSQL from all-databases.sql"
|
|
fi
|
|
fi
|
|
|
|
# Redis restore
|
|
if [[ -f "$src/redis/dump.rdb" ]]; then
|
|
log " Restoring Redis dump..."
|
|
run "mkdir -p '/var/lib/redis'"
|
|
run "cp '$src/redis/dump.rdb' '/var/lib/redis/'"
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown redis:redis /var/lib/redis/dump.rdb 2>/dev/null || true
|
|
systemctl restart redis 2>/dev/null || warn "Redis restart failed"
|
|
fi
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "Databases imported ($size)"
|
|
else
|
|
warn "No databases to import"
|
|
fi
|
|
}
|
|
|
|
import_scripts() {
|
|
section "Importing: Custom Scripts and Units"
|
|
|
|
local src="$WORKDIR/configs/scripts"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# Custom scripts
|
|
for script_dir in "$src"/usr_local_*; do
|
|
[[ -d "$script_dir" ]] || continue
|
|
local target="/usr/local/bin"
|
|
log " Importing scripts to: $target"
|
|
run "mkdir -p '$target'"
|
|
run "cp -r '$script_dir/'* '$target/' 2>/dev/null || true"
|
|
run "chmod +x '$target/'* 2>/dev/null || true"
|
|
done
|
|
|
|
for script_dir in "$src"/root_*; do
|
|
[[ -d "$script_dir" ]] || continue
|
|
local target="/root/scripts"
|
|
log " Importing scripts to: $target"
|
|
run "mkdir -p '$target'"
|
|
run "cp -r '$script_dir/'* '$target/' 2>/dev/null || true"
|
|
run "chmod +x '$target/'* 2>/dev/null || true"
|
|
done
|
|
|
|
# Systemd unit overrides
|
|
if [[ -d "$src/systemd" ]]; then
|
|
log " Importing systemd unit overrides..."
|
|
for unit in "$src/systemd"/*; do
|
|
[[ -e "$unit" ]] || continue
|
|
local unit_name=$(basename "$unit")
|
|
if [[ -d "$unit" ]]; then
|
|
run "mkdir -p '/etc/systemd/system/$unit_name'"
|
|
run "cp -r '$unit/'* '/etc/systemd/system/$unit_name/' 2>/dev/null || true"
|
|
else
|
|
run "cp '$unit' '/etc/systemd/system/$unit_name'"
|
|
fi
|
|
log " Imported: $unit_name"
|
|
done
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
systemctl daemon-reload 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# RC.local
|
|
if [[ -f "$src/rc.local" && -s "$src/rc.local" ]]; then
|
|
log " Importing rc.local..."
|
|
run "cp '$src/rc.local' '/etc/rc.local'"
|
|
run "chmod +x '/etc/rc.local'"
|
|
fi
|
|
|
|
# Environment files
|
|
if [[ -d "$src/environment" ]]; then
|
|
if [[ -f "$src/environment/environment" && -s "$src/environment/environment" ]]; then
|
|
log " Importing /etc/environment..."
|
|
run "cp '$src/environment/environment' '/etc/environment'"
|
|
fi
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
ok "Scripts and units imported ($size)"
|
|
else
|
|
warn "No scripts to import"
|
|
fi
|
|
}
|
|
|
|
import_services() {
|
|
section "Importing: Application Services"
|
|
|
|
local src="$WORKDIR/content/services"
|
|
|
|
if [[ -d "$src" && -n "$(ls -A "$src" 2>/dev/null)" ]]; then
|
|
# Import service directories to /srv/
|
|
for svc_dir in "$src"/*/; do
|
|
[[ -d "$svc_dir" ]] || continue
|
|
local svc_name=$(basename "$svc_dir")
|
|
|
|
# Skip special directories
|
|
[[ "$svc_name" == "git-repos" || "$svc_name" == "docker-compose" || "$svc_name" == "lxc" || "$svc_name" == "streamlit-apps" ]] && continue
|
|
|
|
log " Importing service: $svc_name"
|
|
run "mkdir -p '/srv/$svc_name'"
|
|
run "cp -r '$svc_dir'* '/srv/$svc_name/' 2>/dev/null || true"
|
|
done
|
|
|
|
# Import Streamlit apps
|
|
if [[ -d "$src/streamlit-apps" ]]; then
|
|
log " Importing Streamlit apps..."
|
|
run "mkdir -p '/srv/streamlit/apps'"
|
|
run "cp -r '$src/streamlit-apps/'* '/srv/streamlit/apps/' 2>/dev/null || true"
|
|
fi
|
|
|
|
# Import Git repositories
|
|
if [[ -d "$src/git-repos" ]]; then
|
|
log " Importing Git repositories..."
|
|
run "mkdir -p '/srv/repos'"
|
|
for repo_tar in "$src/git-repos"/*.tar.gz; do
|
|
[[ -f "$repo_tar" ]] || continue
|
|
local repo_name=$(basename "$repo_tar" .tar.gz)
|
|
log " Importing repo: $repo_name"
|
|
run "mkdir -p '/srv/repos/$repo_name'"
|
|
run "tar -xzf '$repo_tar' -C '/srv/repos/$repo_name' --strip-components=2 2>/dev/null || true"
|
|
done
|
|
fi
|
|
|
|
# Import Docker compose files
|
|
if [[ -d "$src/docker-compose" ]]; then
|
|
log " Importing Docker compose files..."
|
|
for compose in "$src/docker-compose"/*; do
|
|
[[ -f "$compose" ]] || continue
|
|
local compose_name=$(basename "$compose")
|
|
local target_path=$(echo "$compose_name" | sed 's/_/\//g' | sed 's/^srv/\/srv/')
|
|
local target_dir=$(dirname "$target_path")
|
|
run "mkdir -p '$target_dir'"
|
|
run "cp '$compose' '$target_path'"
|
|
log " Imported: $compose_name"
|
|
done
|
|
fi
|
|
|
|
# Import LXC configs
|
|
if [[ -d "$src/lxc" ]]; then
|
|
log " Importing LXC container configs..."
|
|
run "mkdir -p '/srv/lxc'"
|
|
for lxc_config in "$src/lxc"/*.config; do
|
|
[[ -f "$lxc_config" ]] || continue
|
|
local container=$(basename "$lxc_config" .config)
|
|
run "mkdir -p '/srv/lxc/$container'"
|
|
run "cp '$lxc_config' '/srv/lxc/$container/config'"
|
|
log " Imported: $container"
|
|
done
|
|
fi
|
|
|
|
# Fix ownership
|
|
if [[ $DRY_RUN -eq 0 ]]; then
|
|
chown -R root:root /srv/* 2>/dev/null || true
|
|
# Fix specific service ownership
|
|
chown -R www-data:www-data /srv/nextcloud 2>/dev/null || true
|
|
chown -R gitea:gitea /srv/gitea 2>/dev/null || true
|
|
fi
|
|
|
|
local size=$(du -sh "$src" 2>/dev/null | cut -f1 || echo "0")
|
|
local svc_count=$(ls -d "$src"/*/ 2>/dev/null | wc -l || echo "0")
|
|
ok "Services imported ($svc_count services, $size)"
|
|
else
|
|
warn "No services to import"
|
|
fi
|
|
}
|
|
|
|
# ── Module selection ──
|
|
declare -A MODULE_FUNCS=(
|
|
[network]=import_network
|
|
[firewall]=import_firewall
|
|
[wireguard]=import_wireguard
|
|
[crowdsec]=import_crowdsec
|
|
[dhcp]=import_dhcp
|
|
[haproxy]=import_haproxy
|
|
[nginx]=import_nginx
|
|
[certs]=import_certs
|
|
[content]=import_content
|
|
[vhosts]=import_vhosts
|
|
[users]=import_users
|
|
[state]=import_state
|
|
[git]=import_git
|
|
[media]=import_media
|
|
[mail]=import_mail
|
|
[accounts]=import_accounts
|
|
[dns]=import_dns
|
|
[databases]=import_databases
|
|
[scripts]=import_scripts
|
|
[services]=import_services
|
|
)
|
|
|
|
ALL_MODULES="network,firewall,wireguard,crowdsec,dhcp,haproxy,nginx,certs,content,vhosts,users,state,git,media,mail,accounts,dns,databases,scripts,services"
|
|
|
|
if [[ "$MODULES" == "all" ]]; then
|
|
MODULES="$ALL_MODULES"
|
|
fi
|
|
|
|
# ── Execute imports ──
|
|
section "Starting Migration Import"
|
|
log "Archive: $ARCHIVE"
|
|
log "Modules: $MODULES"
|
|
[[ $DRY_RUN -eq 1 ]] && log "Mode: DRY-RUN (no changes will be made)"
|
|
|
|
IFS=',' read -ra MOD_ARRAY <<< "$MODULES"
|
|
IMPORTED_MODULES=()
|
|
FAILED_MODULES=()
|
|
|
|
for mod in "${MOD_ARRAY[@]}"; do
|
|
mod=$(echo "$mod" | tr -d ' ')
|
|
if [[ -n "${MODULE_FUNCS[$mod]:-}" ]]; then
|
|
if ${MODULE_FUNCS[$mod]}; then
|
|
IMPORTED_MODULES+=("$mod")
|
|
else
|
|
FAILED_MODULES+=("$mod")
|
|
fi
|
|
else
|
|
warn "Unknown module: $mod"
|
|
fi
|
|
done
|
|
|
|
# ── Summary ──
|
|
section "Import Complete"
|
|
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
log "DRY-RUN completed — no changes were made"
|
|
log "To apply changes, run without --dry-run"
|
|
else
|
|
log "Modules imported: ${IMPORTED_MODULES[*]:-none}"
|
|
[[ ${#FAILED_MODULES[@]} -gt 0 ]] && warn "Modules with issues: ${FAILED_MODULES[*]}"
|
|
|
|
if [[ $SKIP_BACKUP -eq 0 ]]; then
|
|
log "Rollback available at: $ROLLBACK_SNAPSHOT"
|
|
log "To rollback: bash scripts/migration-rollback.sh $ROLLBACK_SNAPSHOT"
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
ok "Migration import completed"
|
|
|
|
# ── Post-import recommendations ──
|
|
section "Recommended Next Steps"
|
|
echo "1. Verify network connectivity:"
|
|
echo " ip addr show"
|
|
echo " ping -c 3 8.8.8.8"
|
|
echo
|
|
echo "2. Check service status:"
|
|
echo " systemctl status secubox-* --no-pager"
|
|
echo
|
|
echo "3. Test WireGuard (if imported):"
|
|
echo " wg show"
|
|
echo
|
|
echo "4. Test CrowdSec (if imported):"
|
|
echo " cscli decisions list"
|
|
echo
|
|
echo "5. Verify firewall rules:"
|
|
echo " nft list ruleset"
|