Update setup_harden_debian_ubuntu.sh

This commit is contained in:
buildplan 2025-06-26 16:49:03 +01:00 committed by GitHub
parent f836af75b4
commit 19723abcf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -83,8 +83,6 @@ print_info() {
echo -e "${PURPLE} $1${NC}" | tee -a "$LOG_FILE"
}
# --- USER INTERACTION ---
confirm() {
local prompt="$1"
local default="${2:-n}"
@ -213,6 +211,8 @@ load_config() {
TAILSCALE_ACCEPT_ROUTES="${TAILSCALE_ACCEPT_ROUTES:-yes}"
SMTP_SERVER="${SMTP_SERVER:-}"
SMTP_PORT="${SMTP_PORT:-}"
SMTP_USER="${SMTP_USER:-}"
SMTP_PASS="${SMTP_PASS:-}"
SMTP_FROM="${SMTP_FROM:-}"
SMTP_TO="${SMTP_TO:-}"
NTFY_SERVER="${NTFY_SERVER:-}"
@ -264,7 +264,7 @@ load_config() {
errors+=("Invalid TAILSCALE_LOGIN_SERVER")
fi
if [[ -z "$TAILSCALE_AUTH_KEY" ]]; then
errors+=("Missing TAILSCALE_AUTH_KEY (required with TAILSCALE_LOGIN_SERVER)")
errors+=("Missing TAILSCALE_AUTH_KEY")
fi
if [[ -z "$TAILSCALE_OPERATOR" ]]; then
errors+=("Missing TAILSCALE_OPERATOR")
@ -272,10 +272,10 @@ load_config() {
errors+=("Invalid TAILSCALE_OPERATOR")
fi
if [[ -n "$TAILSCALE_ACCEPT_DNS" && ! "$TAILSCALE_ACCEPT_DNS" =~ ^(yes|no)$ ]]; then
errors+=("Invalid TAILSCALE_ACCEPT_DNS (must be yes/no)")
errors+=("Invalid TAILSCALE_ACCEPT_DNS")
fi
if [[ -n "$TAILSCALE_ACCEPT_ROUTES" && ! "$TAILSCALE_ACCEPT_ROUTES" =~ ^(yes|no)$ ]]; then
errors+=("Invalid TAILSCALE_ACCEPT_ROUTES (must be yes/no)")
errors+=("Invalid TAILSCALE_ACCEPT_ROUTES")
fi
fi
if [[ -n "$SMTP_SERVER" ]]; then
@ -287,6 +287,12 @@ load_config() {
elif ! validate_smtp_port "$SMTP_PORT"; then
errors+=("Invalid SMTP_PORT")
fi
if [[ -z "$SMTP_USER" ]]; then
errors+=("Missing SMTP_USER")
fi
if [[ -z "$SMTP_PASS" ]]; then
errors+=("Missing SMTP_PASS")
fi
if [[ -z "$SMTP_FROM" ]]; then
errors+=("Missing SMTP_FROM")
elif ! validate_email "$SMTP_FROM"; then
@ -346,6 +352,8 @@ load_config() {
if [[ -n "$SMTP_SERVER" ]]; then
[[ ! "$SMTP_SERVER" =~ ^[a-zA-Z0-9.-]+$ ]] && prompt_smtp_server
[[ -z "$SMTP_PORT" || ! $(validate_smtp_port "$SMTP_PORT") ]] && prompt_smtp_port
[[ -z "$SMTP_USER" ]] && prompt_smtp_user
[[ -z "$SMTP_PASS" ]] && prompt_smtp_pass
[[ -z "$SMTP_FROM" || ! $(validate_email "$SMTP_FROM") ]] && prompt_smtp_from
[[ -z "$SMTP_TO" || ! $(validate_email "$SMTP_TO") ]] && prompt_smtp_to
fi
@ -362,6 +370,8 @@ load_config() {
return 0
}
# --- USER PROMPT FUNCTIONS ---
prompt_username() {
while true; do
read -rp "$(echo -e "${CYAN}Enter username for new admin user: ${NC}")" USERNAME
@ -519,6 +529,25 @@ prompt_smtp_port() {
PROMPTED_SETTINGS+=("SMTP_PORT")
}
prompt_smtp_user() {
read -rp "$(echo -e "${CYAN}Enter SMTP username: ${NC}")" SMTP_USER
if [[ -z "$SMTP_USER" ]]; then
print_error "SMTP username cannot be empty."
SMTP_USER=""
fi
PROMPTED_SETTINGS+=("SMTP_USER")
}
prompt_smtp_pass() {
read -sp "$(echo -e "${CYAN}Enter SMTP password: ${NC}")" SMTP_PASS
echo
if [[ -z "$SMTP_PASS" ]]; then
print_error "SMTP password cannot be empty."
SMTP_PASS=""
fi
PROMPTED_SETTINGS+=("SMTP_PASS")
}
prompt_smtp_from() {
read -rp "$(echo -e "${CYAN}Enter SMTP from email address: ${NC}")" SMTP_FROM
if [[ -z "$SMTP_FROM" || ! $(validate_email "$SMTP_FROM") ]]; then
@ -704,6 +733,8 @@ full_interactive_config() {
prompt_smtp_server
if [[ -n "$SMTP_SERVER" ]]; then
prompt_smtp_port
prompt_smtp_user
prompt_smtp_pass
prompt_smtp_from
prompt_smtp_to
fi
@ -965,7 +996,7 @@ EOF
rm -f "$NEW_SSH_CONFIG"
else
print_info "Creating or updating hardened SSH configuration..."
mv "$NEW_SSH_CONFIG" /etc/ssh/sshd_config.d/99-hardening.conf
mv "$NEW_SSH_CONFIG" /etc/ssh/sshd_config.d/99thor-hardening.conf
chmod 644 /etc/ssh/sshd_config.d/99-hardening.conf
tee /etc/issue.net > /dev/null <<'EOF'
******************************************************************************
@ -1136,6 +1167,234 @@ configure_firewall() {
log "Firewall configuration completed."
}
configure_fail2ban() {
print_section "Fail2Ban Configuration"
if ! dpkg -l fail2ban | grep -q ^ii; then
print_error "fail2ban package is not installed."
exit 1
fi
print_info "Configuring Fail2Ban..."
local jail_config="/etc/fail2ban/jail.d/custom.conf"
mkdir -p /etc/fail2ban/jail.d
cp /etc/fail2ban/jail.conf "$BACKUP_DIR/jail.conf.backup" 2>/dev/null || true
NEW_JAIL_CONFIG=$(mktemp)
tee "$NEW_JAIL_CONFIG" > /dev/null <<EOF
[sshd]
enabled = true
port = $SSH_PORT
maxretry = 3
findtime = 600
bantime = 3600
EOF
if [[ -n "${UFW_PORTS:-}" ]]; then
for port in ${UFW_PORTS//,/ }; do
if [[ "$port" =~ ^[0-9]+/tcp$ ]]; then
port_num="${port%/tcp}"
echo -e "[custom-$port_num]\nenabled = true\nport = $port_num\nmaxretry = 5\nfindtime = 600\nbantime = 3600" >> "$NEW_JAIL_CONFIG"
fi
done
fi
if [[ -f "$jail_config" ]] && cmp -s "$jail_config" "$NEW_JAIL_CONFIG"; then
print_info "Fail2Ban configuration already correct. Skipping."
else
mv "$NEW_JAIL_CONFIG" "$jail_config"
chmod 644 "$jail_config"
systemctl restart fail2ban
if systemctl is-active --quiet fail2ban; then
print_success "Fail2Ban configured and running."
fail2ban-client status sshd | tee -a "$LOG_FILE"
else
print_error "Failed to start Fail2Ban service. Check 'journalctl -u fail2ban'."
exit 1
fi
fi
rm -f "$NEW_JAIL_CONFIG"
log "Fail2Ban configuration completed."
}
configure_auto_updates() {
print_section "Automatic Updates Configuration"
if [[ "$AUTO_UPDATES" != "yes" ]]; then
print_info "Skipping automatic updates configuration."
SKIPPED_SETTINGS+=("automatic updates")
return 0
fi
if ! dpkg -l unattended-upgrades | grep -q ^ii; then
print_error "unattended-upgrades package is not installed."
exit 1
fi
print_info "Configuring unattended-upgrades..."
local config_file="/etc/apt/apt.conf.d/50unattended-upgrades"
cp "$config_file" "$BACKUP_DIR/50unattended-upgrades.backup" 2>/dev/null || true
if ! grep -q "Unattended-Upgrade::Automatic-Reboot" "$config_file"; then
echo 'Unattended-Upgrade::Automatic-Reboot "true";' >> "$config_file"
echo 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' >> "$config_file"
fi
if ! grep -q "Unattended-Upgrade::Mail" "$config_file"; then
if [[ -n "${SMTP_TO:-}" ]]; then
echo "Unattended-Upgrade::Mail \"$SMTP_TO\";" >> "$config_file"
echo 'Unattended-Upgrade::MailReport "on-change";' >> "$config_file"
fi
fi
systemctl restart unattended-upgrades
if systemctl is-active --quiet unattended-upgrades; then
print_success "Automatic updates configured."
else
print_error "Failed to start unattended-upgrades service."
exit 1
fi
log "Automatic updates configuration completed."
}
configure_monitoring() {
print_section "System Monitoring Configuration"
if [[ -n "${SMTP_SERVER:-}" || -n "${NTFY_SERVER:-}" ]]; then
if [[ -n "${SMTP_SERVER:-}" && -n "${SMTP_PORT:-}" && -n "${SMTP_USER:-}" && -n "${SMTP_PASS:-}" && -n "${SMTP_FROM:-}" && -n "${SMTP_TO:-}" ]]; then
print_info "Configuring Postfix for SMTP2go..."
NEW_POSTFIX_CONFIG=$(mktemp)
tee "$NEW_POSTFIX_CONFIG" > /dev/null <<EOF
relayhost = [$SMTP_SERVER]:$SMTP_PORT
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
EOF
if [[ -f /etc/postfix/main.cf ]] && grep -q "relayhost = \[$SMTP_SERVER\]:$SMTP_PORT" /etc/postfix/main.cf; then
print_info "Postfix configuration already correct. Skipping."
else
cat "$NEW_POSTFIX_CONFIG" >> /etc/postfix/main.cf
print_info "Creating Postfix SASL password file..."
if [[ $VERBOSE == true && ( -z "$SMTP_USER" || -z "$SMTP_PASS" ) ]]; then
[[ -z "$SMTP_USER" ]] && prompt_smtp_user
[[ -z "$SMTP_PASS" ]] && prompt_smtp_pass
fi
if [[ -z "$SMTP_USER" || -z "$SMTP_PASS" ]]; then
print_error "SMTP credentials missing. Skipping SMTP configuration."
SKIPPED_SETTINGS+=("SMTP monitoring")
SMTP_SERVER=""
rm -f "$NEW_POSTFIX_CONFIG"
return
fi
echo "[$SMTP_SERVER]:$SMTP_PORT $SMTP_USER:$SMTP_PASS" > /etc/postfix/sasl_passwd
chmod 600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
systemctl restart postfix
print_info "Testing SMTP configuration..."
if echo "Test email from $(hostname)" | mail -s "Test Alert" "$SMTP_TO" 2>&1 | tee -a "$LOG_FILE"; then
print_success "SMTP test email sent to $SMTP_TO."
else
print_error "Failed to send test email. Check /var/log/mail.log."
fi
fi
rm -f "$NEW_POSTFIX_CONFIG"
else
print_info "Incomplete SMTP configuration. Skipping SMTP."
SKIPPED_SETTINGS+=("SMTP monitoring")
SMTP_SERVER=""
fi
if [[ -n "${NTFY_SERVER:-}" && -n "${NTFY_TOKEN:-}" ]]; then
print_info "Configuring ntfy notifications..."
curl -s -o /dev/null -H "Authorization: Bearer $NTFY_TOKEN" "$NTFY_SERVER" || {
print_error "Failed to connect to ntfy server. Check NTFY_SERVER and NTFY_TOKEN."
SKIPPED_SETTINGS+=("ntfy monitoring")
NTFY_SERVER=""
NTFY_TOKEN=""
}
elif [[ -n "${NTFY_SERVER:-}" && -z "${NTFY_TOKEN:-}" && $VERBOSE == true ]]; then
prompt_ntfy_token
if [[ -n "${NTFY_TOKEN:-}" ]]; then
print_info "Configuring ntfy notifications..."
curl -s -o /dev/null -H "Authorization: Bearer $NTFY_TOKEN" "$NTFY_SERVER" || {
print_error "Failed to connect to ntfy server. Check NTFY_SERVER and NTFY_TOKEN."
SKIPPED_SETTINGS+=("ntfy monitoring")
NTFY_SERVER=""
NTFY_TOKEN=""
}
else
print_info "No ntfy token provided. Skipping ntfy."
SKIPPED_SETTINGS+=("ntfy monitoring")
NTFY_SERVER=""
fi
else
print_info "Incomplete ntfy configuration. Skipping ntfy."
SKIPPED_SETTINGS+=("ntfy monitoring")
NTFY_SERVER=""
fi
print_info "Setting up disk space monitoring and rsync backup cron jobs..."
local cron_file="/etc/cron.d/setup_harden_monitoring"
NEW_CRON_CONFIG=$(mktemp)
tee "$NEW_CRON_CONFIG" > /dev/null <<'EOF'
# Disk space monitoring (alert at 90% usage)
*/15 * * * * root df -h / | awk 'NR==2 {if ($5+0 >= 90) {print "Disk usage critical on '"$(hostname)"': " $0}}' | while read -r line; do [[ -n "${SMTP_TO:-}" ]] && echo "$line" | mail -s "Disk Alert" "${SMTP_TO}"; [[ -n "${NTFY_SERVER:-}" && -n "${NTFY_TOKEN:-}" ]] && curl -s -H "Authorization: Bearer ${NTFY_TOKEN}" -d "$line" "${NTFY_SERVER}"; done
# Rsync backup to /root/backup (daily at 1am)
0 1 * * * root rsync -a --delete --exclude '/root/backup' / /root/backup && { [[ -n "${SMTP_TO:-}" ]] && echo "Rsync backup completed on $(hostname)" | mail -s "Backup Complete" "${SMTP_TO}"; [[ -n "${NTFY_SERVER:-}" && -n "${NTFY_TOKEN:-}" ]] && curl -s -H "Authorization: Bearer ${NTFY_TOKEN}" -d "Rsync backup completed on $(hostname)" "${NTFY_SERVER}"; }
EOF
if [[ -f "$cron_file" ]] && cmp -s "$cron_file" "$NEW_CRON_CONFIG"; then
print_info "Cron jobs already configured. Skipping."
else
mv "$NEW_CRON_CONFIG" "$cron_file"
chmod 644 "$cron_file"
print_success "Disk space and backup cron jobs configured."
fi
rm -f "$NEW_CRON_CONFIG"
else
print_info "Skipping system monitoring configuration (no SMTP or ntfy settings provided)."
SKIPPED_SETTINGS+=("system monitoring")
fi
log "System monitoring configuration completed."
}
install_docker() {
if [[ "$INSTALL_DOCKER" != "yes" ]]; then
print_info "Skipping Docker installation."
SKIPPED_SETTINGS+=("Docker installation")
return 0
fi
print_section "Docker Installation"
if command -v docker >/dev/null 2>&1; then
print_info "Docker already installed."
if ! groups "$USERNAME" | grep -qw docker; then
print_info "Adding '$USERNAME' to docker group..."
usermod -aG docker "$USERNAME"
print_success "User '$USERNAME' added to docker group."
else
print_info "User '$USERNAME' already in docker group."
fi
return 0
fi
print_info "Installing Docker..."
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
chmod +x /tmp/get-docker.sh
if ! grep -q "docker" /tmp/get-docker.sh; then
print_error "Downloaded Docker install script appears invalid."
rm -f /tmp/get-docker.sh
exit 1
fi
if ! sh /tmp/get-docker.sh; then
print_error "Failed to install Docker."
rm -f /tmp/get-docker.sh
exit 1
fi
rm -f /tmp/get-docker.sh
systemctl enable docker
systemctl start docker
print_info "Adding '$USERNAME' to docker group..."
usermod -aG docker "$USERNAME"
if groups "$USERNAME" | grep -qw docker; then
print_success "User '$USERNAME' added to docker group."
else
print_error "Failed to add '$USERNAME' to docker group."
exit 1
fi
if ! docker ps >/dev/null 2>&1; then
print_error "Docker service is not running or accessible. Check 'systemctl status docker'."
exit 1
fi
print_success "Docker installation completed."
log "Docker installation and configuration completed."
}
install_tailscale() {
if [[ "$INSTALL_TAILSCALE" != "yes" ]]; then
print_info "Skipping Tailscale installation."
@ -1153,35 +1412,32 @@ install_tailscale() {
[[ -n "${TAILSCALE_AUTH_KEY:-}" ]] && ts_cmd="$ts_cmd --authkey=$TAILSCALE_AUTH_KEY"
ts_cmd="$ts_cmd --hostname=$SERVER_NAME"
if eval "$ts_cmd" 2>&1 | tee -a "$LOG_FILE"; then
if sudo tailscale status | grep -q "connected"; then
print_success "Tailscale connected successfully."
if tailscale status | grep -q "$SERVER_NAME"; then
print_success "Tailscale configured and connected."
tailscale status | tee -a "$LOG_FILE"
else
print_error "Tailscale connection failed. Run 'sudo tailscale up' manually."
print_error "Tailscale failed to connect. Check 'tailscale status' and log file."
exit 1
fi
else
print_error "Failed to run Tailscale up. Check log for details."
print_error "Failed to configure Tailscale. Check log file."
exit 1
fi
else
print_info "Incomplete Tailscale configuration. Skipping Headscale setup."
SKIPPED_SETTINGS+=("Tailscale Headscale configuration")
print_info "Tailscale configuration incomplete. Skipping configuration."
SKIPPED_SETTINGS+=("Tailscale configuration")
fi
return 0
fi
print_info "Installing Tailscale..."
curl -fsSL https://tailscale.com/install.sh -o /tmp/tailscale_install.sh
chmod +x /tmp/tailscale_install.sh
if ! grep -q "tailscale" /tmp/tailscale_install.sh; then
print_error "Downloaded Tailscale install script appears invalid."
rm -f /tmp/tailscale_install.sh
exit 1
fi
if ! /tmp/tailscale_install.sh; then
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | gpg --dearmor -o /usr/share/keyrings/tailscale-archive-keyring.gpg
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list -o /etc/apt/sources.list.d/tailscale.list
if ! apt-get update -qq || ! apt-get install -y -qq tailscale; then
print_error "Failed to install Tailscale."
rm -f /tmp/tailscale_install.sh
exit 1
fi
rm -f /tmp/tailscale_install.sh
systemctl enable --now tailscaled
systemctl enable tailscaled
systemctl start tailscaled
if [[ -n "${TAILSCALE_LOGIN_SERVER:-}" && -n "${TAILSCALE_AUTH_KEY:-}" && -n "${TAILSCALE_OPERATOR:-}" ]]; then
print_info "Configuring Tailscale with Headscale..."
local ts_cmd="tailscale up --login-server=$TAILSCALE_LOGIN_SERVER --operator=$TAILSCALE_OPERATOR"
@ -1190,17 +1446,20 @@ install_tailscale() {
[[ -n "${TAILSCALE_AUTH_KEY:-}" ]] && ts_cmd="$ts_cmd --authkey=$TAILSCALE_AUTH_KEY"
ts_cmd="$ts_cmd --hostname=$SERVER_NAME"
if eval "$ts_cmd" 2>&1 | tee -a "$LOG_FILE"; then
if sudo tailscale status | grep -q "connected"; then
print_success "Tailscale connected successfully."
if tailscale status | grep -q "$SERVER_NAME"; then
print_success "Tailscale configured and connected."
tailscale status | tee -a "$LOG_FILE"
else
print_error "Tailscale connection failed. Run 'sudo tailscale up' manually."
print_error "Tailscale failed to connect. Check 'tailscale status' and log file."
exit 1
fi
else
print_error "Failed to run Tailscale up. Check log for details."
print_error "Failed to configure Tailscale. Check log file."
exit 1
fi
else
print_info "Incomplete Tailscale configuration. Skipping Headscale setup."
SKIPPED_SETTINGS+=("Tailscale Headscale configuration")
print_info "Tailscale installed but not configured (missing login server, auth key, or operator)."
SKIPPED_SETTINGS+=("Tailscale configuration")
fi
print_success "Tailscale installation completed."
log "Tailscale installation and configuration completed."
@ -1209,7 +1468,7 @@ install_tailscale() {
configure_swap() {
print_section "Swap Configuration"
if [[ "$IS_CONTAINER" == true ]]; then
print_info "Container environment detected. Skipping swap configuration."
print_info "Skipping swap configuration in container environment."
SKIPPED_SETTINGS+=("swap configuration")
return 0
fi
@ -1223,47 +1482,36 @@ configure_swap() {
SKIPPED_SETTINGS+=("swap configuration")
return 0
fi
local swap_size_bytes=$(convert_to_bytes "$SWAP_SIZE")
if [[ -f /swapfile ]]; then
CURRENT_SWAP_SIZE=$(ls -lh /swapfile | awk '{print $5}')
print_info "Swap file already exists with size $CURRENT_SWAP_SIZE."
if [[ "$CURRENT_SWAP_SIZE" == "$SWAP_SIZE" ]]; then
print_info "Swap size matches requested size. Skipping."
current_size=$(ls -l /swapfile | awk '{print $5}')
if [[ "$current_size" -eq "$swap_size_bytes" ]]; then
print_info "Swap file already exists with correct size ($SWAP_SIZE)."
return 0
else
print_info "Removing existing swap file to create new one..."
print_info "Removing existing swap file..."
swapoff /swapfile 2>/dev/null || true
rm -f /swapfile
sed -i '/\/swapfile/d' /etc/fstab
fi
fi
print_info "Creating swap file of size $SWAP_SIZE..."
SWAP_BYTES=$(convert_to_bytes "$SWAP_SIZE")
if [[ $SWAP_BYTES -eq 0 ]]; then
print_error "Invalid swap size calculation. Skipping."
SKIPPED_SETTINGS+=("swap configuration")
return 0
fi
if ! fallocate -l "$SWAP_SIZE" /swapfile; then
if ! fallocate -l "$swap_size_bytes" /swapfile; then
print_error "Failed to create swap file. Check disk space."
exit 1
fi
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
if ! grep -q "/swapfile" /etc/fstab; then
if ! grep -q "^/swapfile" /etc/fstab; then
echo "/swapfile none swap sw 0 0" >> /etc/fstab
fi
print_info "Configuring swappiness..."
sysctl vm.swappiness=10
if ! grep -q "vm.swappiness" /etc/sysctl.conf; then
echo "vm.swappiness=10" >> /etc/sysctl.conf
echo "vm.swappiness=10" >> /etc/sysctl.conf
if free | grep -q "Swap:"; then
print_success "Swap configured: $SWAP_SIZE"
free -h | tee -a "$LOG_FILE"
else
sed -i 's/.*vm\.swappiness.*/vm.swappiness=10/' /etc/sysctl.conf
fi
if swapon --show | grep -q "/swapfile"; then
print_success "Swap file of $SWAP_SIZE configured."
else
print_error "Swap activation failed. Check 'swapon --show'."
print_error "Failed to configure swap. Check 'free -h' and log file."
exit 1
fi
log "Swap configuration completed."
@ -1275,75 +1523,66 @@ configure_time() {
print_error "chrony package is not installed."
exit 1
fi
print_info "Configuring time synchronization with chrony..."
systemctl enable chrony
systemctl restart chrony
sleep 2
systemctl start chrony
if systemctl is-active --quiet chrony; then
print_success "Time synchronization enabled with chrony."
chronyc sources | tee -a "$LOG_FILE"
print_success "Time synchronization configured."
chronyc tracking | tee -a "$LOG_FILE"
else
print_error "Failed to start chrony service."
print_error "Failed to start chrony service. Check 'journalctl -u chrony'."
exit 1
fi
log "Time synchronization configuration completed."
}
cleanup() {
print_section "System Cleanup"
print_info "Cleaning up package cache..."
apt-get autoremove -y -qq
apt-get autoclean -y -qq
print_info "Removing temporary files..."
find /tmp -type f -atime +1 -delete
print_info "Clearing log files..."
print_section "Cleanup"
print_info "Cleaning up package cache and temporary files..."
apt-get clean
rm -rf /var/lib/apt/lists/*
rm -f /tmp/*.sh
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
print_success "System cleanup completed."
log "System cleanup completed."
print_success "Cleanup completed."
log "Cleanup completed."
}
print_summary() {
print_section "Setup Summary"
echo -e "${GREEN}Setup completed successfully!${NC}" | tee -a "$LOG_FILE"
echo -e "${CYAN}Configuration Details:${NC}" | tee -a "$LOG_FILE"
echo -e " - Username: $USERNAME" | tee -a "$LOG_FILE"
echo -e " - Hostname: $SERVER_NAME" | tee -a "$LOG_FILE"
echo -e " - SSH Port: $SSH_PORT" | tee -a "$LOG_FILE"
echo -e " - Server IP: $SERVER_IP" | tee -a "$LOG_FILE"
echo -e " - Timezone: $TIMEZONE" | tee -a "$LOG_FILE"
[[ -n "${SWAP_SIZE:-}" && "$IS_CONTAINER" == false ]] && echo -e " - Swap Size: $SWAP_SIZE" | tee -a "$LOG_FILE"
[[ -n "${UFW_PORTS:-}" ]] && echo -e " - UFW Ports: $UFW_PORTS" | tee -a "$LOG_FILE"
[[ "$AUTO_UPDATES" == "yes" ]] && echo -e " - Automatic Updates: Enabled" | tee -a "$LOG_FILE"
[[ "$INSTALL_DOCKER" == "yes" ]] && echo -e " - Docker: Installed" | tee -a "$LOG_FILE"
[[ "$INSTALL_TAILSCALE" == "yes" ]] && echo -e " - Tailscale: Installed" | tee -a "$LOG_FILE"
[[ -n "${TAILSCALE_LOGIN_SERVER:-}" ]] && echo -e " - Tailscale Login Server: $TAILSCALE_LOGIN_SERVER" | tee -a "$LOG_FILE"
[[ -n "${SMTP_SERVER:-}" ]] && echo -e " - SMTP Server: $SMTP_SERVER:$SMTP_PORT" | tee -a "$LOG_FILE"
[[ -n "${NTFY_SERVER:-}" ]] && echo -e " - ntfy Server: $NTFY_SERVER" | tee -a "$LOG_FILE"
echo -e "\n${YELLOW}Configuration Applied:${NC}"
echo -e " Username: $USERNAME"
echo -e " Hostname: $SERVER_NAME"
echo -e " SSH Port: $SSH_PORT"
echo -e " Server IP: $SERVER_IP"
echo -e " Timezone: $TIMEZONE"
[[ -n "${SWAP_SIZE:-}" && "$IS_CONTAINER" == false ]] && echo -e " Swap Size: $SWAP_SIZE"
[[ -n "${UFW_PORTS:-}" ]] && echo -e " UFW Ports: $UFW_PORTS"
[[ "$AUTO_UPDATES" == "yes" ]] && echo -e " Auto Updates: Enabled"
[[ "$INSTALL_DOCKER" == "yes" ]] && echo -e " Docker: Installed"
[[ "$INSTALL_TAILSCALE" == "yes" ]] && echo -e " Tailscale: Installed"
[[ -n "${TAILSCALE_LOGIN_SERVER:-}" ]] && echo -e " Tailscale Server: $TAILSCALE_LOGIN_SERVER"
[[ -n "${SMTP_SERVER:-}" ]] && echo -e " SMTP Server: $SMTP_SERVER"
[[ -n "${NTFY_SERVER:-}" ]] && echo -e " ntfy Server: $NTFY_SERVER"
if [[ ${#PROMPTED_SETTINGS[@]} -gt 0 ]]; then
echo -e " - Prompted Settings: ${PROMPTED_SETTINGS[*]}" | tee -a "$LOG_FILE"
echo -e "\n${CYAN}Prompted Settings:${NC} ${PROMPTED_SETTINGS[*]}"
fi
if [[ ${#SKIPPED_SETTINGS[@]} -gt 0 ]]; then
echo -e " - Skipped Settings: ${SKIPPED_SETTINGS[*]}" | tee -a "$LOG_FILE"
echo -e "\n${YELLOW}Skipped Settings:${NC} ${SKIPPED_SETTINGS[*]}"
fi
echo -e "\n${YELLOW}Log file: $LOG_FILE${NC}" | tee -a "$LOG_FILE"
echo -e "${YELLOW}Backup directory: $BACKUP_DIR${NC}" | tee -a "$LOG_FILE"
echo -e "\n${CYAN}Verification Steps:${NC}" | tee -a "$LOG_FILE"
echo -e " 1. Verify SSH: ssh -p $SSH_PORT $USERNAME@$SERVER_IP" | tee -a "$LOG_FILE"
echo -e " 2. Check firewall: sudo ufw status verbose" | tee -a "$LOG_FILE"
echo -e " 3. Check services: systemctl status ssh fail2ban chrony" | tee -a "$LOG_FILE"
[[ "$INSTALL_DOCKER" == "yes" ]] && echo -e " 3.1 Check Docker: systemctl status docker; docker ps" | tee -a "$LOG_FILE"
[[ "$INSTALL_TAILSCALE" == "yes" ]] && echo -e " 3.2 Check Tailscale: sudo tailscale status" | tee -a "$LOG_FILE"
[[ -n "${SMTP_SERVER:-}" ]] && echo -e " 3.3 Check Postfix: systemctl status postfix; tail /var/log/mail.log" | tee -a "$LOG_FILE"
echo -e "\n${YELLOW}ACTION REQUIRED:${NC}" | tee -a "$LOG_FILE"
echo -e " - Check your VPS provider's edge firewall to allow opened ports (e.g., $SSH_PORT/tcp)." | tee -a "$LOG_FILE"
echo -e " - Reboot recommended: sudo reboot" | tee -a "$LOG_FILE"
echo -e "\n${YELLOW}Log File:${NC} $LOG_FILE"
echo -e "${YELLOW}Backups:${NC} $BACKUP_DIR"
echo -e "\n${YELLOW}Next Steps:${NC}"
echo -e " 1. Verify SSH: ssh -p $SSH_PORT $USERNAME@$SERVER_IP"
echo -e " 2. Check Firewall: sudo ufw status verbose"
echo -e " 3. Check Services: systemctl status ssh fail2ban chrony docker tailscaled postfix"
echo -e " 4. Reboot (recommended): sudo reboot"
echo -e "\n${YELLOW}Note:${NC} If using a VPS (e.g., DigitalOcean, AWS), ensure the edge firewall allows port $SSH_PORT and other configured ports."
log "Setup summary completed."
}
main() {
print_header
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
chmod 640 "$LOG_FILE"
check_system
check_dependencies
collect_config
@ -1363,4 +1602,6 @@ main() {
print_summary
}
# Execute main function
main
```