Merge pull request #24 from buildplan/test

ssh config improvements
This commit is contained in:
buildplan 2025-06-30 23:12:57 +01:00 committed by GitHub
commit e72b648fe9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 480 additions and 117 deletions

View File

@ -1,6 +1,6 @@
# Debian & Ubuntu Server Setup & Hardening Script
**Version:** v0.51
**Version:** v0.52
**Last Updated:** 2025-06-30
@ -75,7 +75,7 @@ sha256sum du_setup.sh
Compare the output hash to the one below. They must match exactly.
`fa2bbc20e5e658ed0a485d9bcb78f7c2f28501ed4363f759f1dd2bb75a395e58`
`dbe2abf3dd0dee253988e8e53e7a91970a7c07ff97f9fa446f326667297c43de`
### 3\. Run the Script

View File

@ -1,8 +1,9 @@
#!/bin/bash
# Debian 12 and Ubuntu Server Hardening Interactive Script
# Version: 0.51 | 2025-06-30
# Version: 0.52 | 2025-06-30
# Changelog:
# - v0.52: Roll-back SSH config on failure to configure SSH port, confirmed SSH config support for Ubuntu 24.10
# - v0.51: corrected repo links
# - v0.50: versioning format change and repo name change
# - v4.3: Add SHA256 integrity verification
@ -90,7 +91,7 @@ print_header() {
echo -e "${CYAN}╔═════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}║ ║${NC}"
echo -e "${CYAN}║ DEBIAN/UBUNTU SERVER SETUP AND HARDENING SCRIPT ║${NC}"
echo -e "${CYAN}║ v0.51 | 2025-06-30 ║${NC}"
echo -e "${CYAN}║ v0.52 | 2025-06-30 ║${NC}"
echo -e "${CYAN}║ ║${NC}"
echo -e "${CYAN}╚═════════════════════════════════════════════════════════════════╝${NC}"
echo
@ -503,17 +504,31 @@ configure_system() {
log "System configuration completed."
}
cleanup_and_exit() {
local exit_code=$?
if [[ $exit_code -ne 0 && $(type -t rollback_ssh_changes) == "function" ]]; then
print_error "An error occurred. Rolling back SSH changes to port $PREVIOUS_SSH_PORT..."
rollback_ssh_changes
if [[ $? -ne 0 ]]; then
print_error "Rollback failed. SSH may not be accessible. Please check 'systemctl status $SSH_SERVICE' and 'journalctl -u $SSH_SERVICE'."
fi
fi
exit $exit_code
}
configure_ssh() {
trap cleanup_and_exit ERR
print_section "SSH Hardening"
local CURRENT_SSH_PORT USER_HOME SSH_DIR SSH_KEY AUTH_KEYS NEW_SSH_CONFIG
local CURRENT_SSH_PORT USER_HOME SSH_DIR SSH_KEY AUTH_KEYS NEW_SSH_CONFIG PREVIOUS_SSH_PORT
# Ensure openssh-server is installed
if ! dpkg -l openssh-server | grep -q ^ii; then
print_error "openssh-server package is not installed. Please ensure it is installed."
exit 1
print_error "openssh-server package is not installed."
return 1
fi
# Detect SSH service name, preserve socket activation on Ubuntu if active
# Detect SSH service name
if [[ $ID == "ubuntu" ]] && systemctl is-active ssh.socket >/dev/null 2>&1; then
SSH_SERVICE="ssh.socket"
print_info "Using SSH socket activation: $SSH_SERVICE"
@ -521,105 +536,58 @@ configure_ssh() {
SSH_SERVICE="ssh.service"
elif systemctl is-enabled sshd.service >/dev/null 2>&1 || systemctl is-active sshd.service >/dev/null 2>&1; then
SSH_SERVICE="sshd.service"
elif ps aux | grep -q "[s]shd"; then
print_warning "SSH daemon running but no standard service detected."
SSH_SERVICE="ssh.service" # Default for Debian
if ! systemctl enable --now "$SSH_SERVICE" >/dev/null 2>&1; then
print_error "Failed to enable and start $SSH_SERVICE. Attempting manual start..."
if ! /usr/sbin/sshd; then
print_error "Failed to start SSH daemon manually."
exit 1
fi
print_success "SSH daemon started manually."
fi
else
print_error "No SSH service or daemon detected. Please verify openssh-server installation and daemon status."
exit 1
print_error "No SSH service or daemon detected."
return 1
fi
print_info "Using SSH service: $SSH_SERVICE"
log "Detected SSH service: $SSH_SERVICE"
# Ensure SSH service is enabled and running
if ! systemctl is-enabled "$SSH_SERVICE" >/dev/null 2>&1; then
if ! systemctl enable "$SSH_SERVICE" >/dev/null 2>&1; then
print_error "Failed to enable $SSH_SERVICE. Please check service status."
exit 1
fi
print_success "SSH service enabled: $SSH_SERVICE"
fi
if ! systemctl is-active "$SSH_SERVICE" >/dev/null 2>&1; then
if ! systemctl start "$SSH_SERVICE" >/dev/null 2>&1; then
print_error "Failed to start $SSH_SERVICE. Attempting manual start..."
if ! /usr/sbin/sshd; then
print_error "Failed to start SSH daemon manually."
exit 1
fi
print_success "SSH daemon started manually."
fi
fi
CURRENT_SSH_PORT=$(ss -tuln | grep -E ":(22|.*$SSH_SERVICE.*)" | awk '{print $5}' | cut -d':' -f2 | head -n1 || echo "22")
# Store the current active port as the previous port
PREVIOUS_SSH_PORT=$(ss -tuln | grep -E ":(22|.*$SSH_SERVICE.*)" | awk '{print $5}' | cut -d':' -f2 | head -n1 || echo "22")
CURRENT_SSH_PORT=$PREVIOUS_SSH_PORT
USER_HOME=$(getent passwd "$USERNAME" | cut -d: -f6)
SSH_DIR="$USER_HOME/.ssh"
SSH_KEY="$SSH_DIR/id_ed25519"
AUTH_KEYS="$SSH_DIR/authorized_keys"
if [[ $LOCAL_KEY_ADDED == false ]] && [[ ! -s "$AUTH_KEYS" ]]; then
print_info "No local key provided and no existing keys found. Generating new SSH key..."
mkdir -p "$SSH_DIR"
chmod 700 "$SSH_DIR"
sudo -u "$USERNAME" ssh-keygen -t ed25519 -f "$SSH_KEY" -N "" -q
cat "$SSH_KEY.pub" >> "$AUTH_KEYS"
chmod 600 "$AUTH_KEYS"
chown -R "$USERNAME:$USERNAME" "$SSH_DIR"
print_info "No local key provided. Generating new SSH key..."
mkdir -p "$SSH_DIR"; chmod 700 "$SSH_DIR"
sudo -u "$USERNAME" ssh-keygen -t ed25519 -f "$SSH_DIR/id_ed25519" -N "" -q
cat "$SSH_DIR/id_ed25519.pub" >> "$AUTH_KEYS"
chmod 600 "$AUTH_KEYS"; chown -R "$USERNAME:$USERNAME" "$SSH_DIR"
print_success "SSH key generated."
echo -e "${YELLOW}Public key for remote access:${NC}"
cat "$SSH_KEY.pub" | tee -a "$LOG_FILE"
echo -e "${YELLOW}Copy this key to your local ~/.ssh/authorized_keys or use 'ssh-copy-id -p $CURRENT_SSH_PORT $USERNAME@$SERVER_IP' from your local machine.${NC}"
else
print_info "SSH key(s) already present or added. Skipping key generation."
echo -e "${YELLOW}Public key for remote access:${NC}"; cat "$SSH_DIR/id_ed25519.pub"
fi
print_warning "SSH Key Authentication Required for Next Steps!"
echo -e "${CYAN}Test SSH access from a SEPARATE terminal now: ssh -p $CURRENT_SSH_PORT $USERNAME@$SERVER_IP${NC}"
if ! confirm "Can you successfully log in using your SSH key?"; then
print_error "SSH key authentication is mandatory to proceed. Please fix and re-run."
exit 1
print_error "SSH key authentication is mandatory to proceed."
return 1
fi
print_info "Backing up original SSH config..."
SSHD_BACKUP_FILE="$BACKUP_DIR/sshd_config.backup_$(date +%Y%m%d_%H%M%S)"
cp /etc/ssh/sshd_config "$SSHD_BACKUP_FILE"
# Apply port override based on SSH service type
if [[ "$SSH_SERVICE" == "ssh.socket" ]]; then
# Apply port override
if [[ $ID == "ubuntu" ]] && dpkg --compare-versions "$(lsb_release -rs)" ge "24.04"; then
print_info "Updating SSH port in /etc/ssh/sshd_config for Ubuntu 24.04+..."
if ! grep -q "^Port" /etc/ssh/sshd_config; then echo "Port $SSH_PORT" >> /etc/ssh/sshd_config; else sed -i "s/^Port .*/Port $SSH_PORT/" /etc/ssh/sshd_config; fi
elif [[ "$SSH_SERVICE" == "ssh.socket" ]]; then
print_info "Configuring SSH socket to listen on port $SSH_PORT..."
NEW_SSH_CONFIG=$(mktemp)
tee "$NEW_SSH_CONFIG" > /dev/null <<EOF
[Socket]
ListenStream=
ListenStream=$SSH_PORT
EOF
mkdir -p /etc/systemd/system/ssh.socket.d
mv "$NEW_SSH_CONFIG" /etc/systemd/system/ssh.socket.d/override.conf
chmod 644 /etc/systemd/system/ssh.socket.d/override.conf
echo -e "[Socket]\nListenStream=\nListenStream=$SSH_PORT" > /etc/systemd/system/ssh.socket.d/override.conf
else
print_info "Configuring SSH service to listen on port $SSH_PORT..."
NEW_SSH_CONFIG=$(mktemp)
tee "$NEW_SSH_CONFIG" > /dev/null <<EOF
[Service]
ExecStart=
ExecStart=/usr/sbin/sshd -D -p $SSH_PORT
EOF
mkdir -p /etc/systemd/system/ssh.service.d
mv "$NEW_SSH_CONFIG" /etc/systemd/system/ssh.service.d/override.conf
chmod 644 /etc/systemd/system/ssh.service.d/override.conf
mkdir -p /etc/systemd/system/${SSH_SERVICE}.d
echo -e "[Service]\nExecStart=\nExecStart=/usr/sbin/sshd -D -p $SSH_PORT" > /etc/systemd/system/${SSH_SERVICE}.d/override.conf
fi
# Apply additional hardening via sshd_config.d
NEW_SSH_CONFIG=$(mktemp)
tee "$NEW_SSH_CONFIG" > /dev/null <<EOF
# Apply additional hardening
mkdir -p /etc/ssh/sshd_config.d
tee /etc/ssh/sshd_config.d/99-hardening.conf > /dev/null <<EOF
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
@ -629,51 +597,28 @@ X11Forwarding no
PrintMotd no
Banner /etc/issue.net
EOF
if [[ -f /etc/ssh/sshd_config.d/99-hardening.conf ]] && cmp -s "$NEW_SSH_CONFIG" /etc/ssh/sshd_config.d/99-hardening.conf; then
print_info "SSH configuration already hardened. Skipping."
rm -f "$NEW_SSH_CONFIG"
else
print_info "Creating or updating hardened SSH configuration..."
mkdir -p /etc/ssh/sshd_config.d
mv "$NEW_SSH_CONFIG" /etc/ssh/sshd_config.d/99-hardening.conf
chmod 644 /etc/ssh/sshd_config.d/99-hardening.conf
tee /etc/issue.net > /dev/null <<'EOF'
tee /etc/issue.net > /dev/null <<'EOF'
******************************************************************************
🔒AUTHORIZED ACCESS ONLY
════ all attempts are logged and reviewed ════
******************************************************************************
EOF
fi
print_info "Reloading systemd and restarting SSH service..."
systemctl daemon-reload
if ! systemctl restart "$SSH_SERVICE"; then
print_error "SSH service failed to restart! Reverting changes..."
rm -f /etc/systemd/system/ssh.service.d/override.conf
cp "$SSHD_BACKUP_FILE" /etc/ssh/sshd_config
rm -f /etc/ssh/sshd_config.d/99-hardening.conf
systemctl daemon-reload
systemctl restart "$SSH_SERVICE" || /usr/sbin/sshd || true
exit 1
fi
# Wait and verify port binding
systemctl restart "$SSH_SERVICE"
sleep 5
if ! ss -tuln | grep -q ":$SSH_PORT"; then
print_error "SSH not listening on port $SSH_PORT after restart! Reverting changes..."
rm -f /etc/systemd/system/ssh.service.d/override.conf
cp "$SSHD_BACKUP_FILE" /etc/ssh/sshd_config
rm -f /etc/ssh/sshd_config.d/99-hardening.conf
systemctl daemon-reload
systemctl restart "$SSH_SERVICE" || /usr/sbin/sshd || true
exit 1
print_error "SSH not listening on port $SSH_PORT after restart!"
return 1
fi
print_success "SSH service restarted on port $SSH_PORT."
# Verify root SSH is disabled
print_info "Verifying root SSH login is disabled..."
if ssh -p "$SSH_PORT" -o BatchMode=yes -o ConnectTimeout=5 root@localhost true 2>/dev/null; then
print_error "Root SSH login is still possible! Check SSH configuration."
exit 1
if ssh -p "$SSH_PORT" -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@localhost true 2>/dev/null; then
print_error "Root SSH login is still possible! Check configuration."
return 1
else
print_success "Confirmed: Root SSH login is disabled."
fi
@ -686,6 +631,7 @@ EOF
local max_retries=3
while (( retry_count < max_retries )); do
if confirm "Was the new SSH connection successful?"; then
print_success "SSH hardening confirmed and finalized."
break
else
(( retry_count++ ))
@ -693,19 +639,60 @@ EOF
print_info "Retrying SSH connection test ($retry_count/$max_retries)..."
sleep 5
else
print_error "Aborting. Restoring original SSH configuration."
rm -f /etc/systemd/system/ssh.service.d/override.conf
cp "$SSHD_BACKUP_FILE" /etc/ssh/sshd_config
rm -f /etc/ssh/sshd_config.d/99-hardening.conf
systemctl daemon-reload
systemctl restart "$SSH_SERVICE" || /usr/sbin/sshd || true
exit 1
print_error "All retries failed. Initiating rollback to port $PREVIOUS_SSH_PORT..."
rollback_ssh_changes
if ! ss -tuln | grep -q ":$PREVIOUS_SSH_PORT"; then
print_error "Rollback failed. SSH not restored on original port $PREVIOUS_SSH_PORT."
else
print_success "Rollback successful. SSH restored on original port $PREVIOUS_SSH_PORT."
fi
return 1
fi
fi
done
trap - ERR
log "SSH hardening completed."
}
rollback_ssh_changes() {
print_info "Rolling back SSH configuration changes to port $PREVIOUS_SSH_PORT..."
rm -f /etc/systemd/system/ssh.service.d/override.conf
rm -f /etc/systemd/system/ssh.socket.d/override.conf
rm -f /etc/ssh/sshd_config.d/99-hardening.conf
if [[ -f "$SSHD_BACKUP_FILE" ]]; then
cp "$SSHD_BACKUP_FILE" /etc/ssh/sshd_config
print_info "Restored original sshd_config from $SSHD_BACKUP_FILE."
else
print_error "Backup file not found. Could not restore sshd_config."
return 1
fi
print_info "Reloading systemd and restarting $SSH_SERVICE..."
systemctl daemon-reload
if ! systemctl restart "$SSH_SERVICE"; then
print_warning "Failed to restart $SSH_SERVICE after rollback. Attempting manual start..."
/usr/sbin/sshd || true
fi
local rollback_verified=false
print_info "Verifying SSH rollback to port $PREVIOUS_SSH_PORT..."
for ((i=1; i<=10; i++)); do
if ss -tuln | grep -q ":$PREVIOUS_SSH_PORT"; then
print_success "Rollback successful. SSH is now listening on port $PREVIOUS_SSH_PORT."
rollback_verified=true
break
fi
sleep 1
done
if [[ $rollback_verified == false ]]; then
print_error "Rollback failed. SSH service is not listening on port $PREVIOUS_SSH_PORT."
print_info "Please check service status manually with 'systemctl status $SSH_SERVICE' and 'journalctl -u $SSH_SERVICE'."
return 1
fi
}
configure_firewall() {
print_section "Firewall Configuration (UFW)"
if ufw status | grep -q "Status: active"; then

View File

@ -1 +1 @@
fa2bbc20e5e658ed0a485d9bcb78f7c2f28501ed4363f759f1dd2bb75a395e58 du_setup.sh
dbe2abf3dd0dee253988e8e53e7a91970a7c07ff97f9fa446f326667297c43de du_setup.sh

376
guide.md Normal file
View File

@ -0,0 +1,376 @@
### **Setup**
- **Environment**: A fresh VM running **Ubuntu 22.04 LTS** (a supported OS) with:
- Root privileges (`sudo` or direct root access).
- Internet connectivity (for package downloads and Tailscale).
- At least 2GB free disk space (for swap and temporary files).
- Minimal installation (no prior SSH hardening, UFW, or Fail2Ban configured).
- **Script Version**: v0.52
- **Execution Mode**: Interactive (not `--quiet`), to capture user prompts and verify decision points.
---
### **Walkthrough**
#### **1. Preparation**
- **Download and Permissions**:
- The README instructs downloading with `wget https://raw.githubusercontent.com/buildplan/du_setup/refs/heads/main/du_setup.sh` and setting `chmod +x du_setup.sh`.
- Assumed command: `sudo ./du_setup.sh`.
- The script starts with `#!/bin/bash` and `set -euo pipefail`, ensuring strict error handling.
- **Log File Creation**:
- The script creates `/var/log/du_setup_$(date +%Y%m%d_%H%M%S).log` (e.g., `/var/log/du_setup_20250630_222800.log`) with `chmod 600`.
- Backup directory `/root/setup_harden_backup_20250630_222800` is created with `chmod 700`.\c
#### **2. Main Function Execution**
##### **check_dependencies**
- **Logic**: Checks for `curl`, `sudo`, and `gpg`. Installs missing dependencies via `apt-get`.
- **Simulation**:
- On a fresh Ubuntu 22.04 VM, `sudo` and `curl` are typically present, but `gpg` might be missing in minimal installs.
- The script runs `apt-get install -y -qq gpg` if needed, which should succeed given internet access.
- **Expected Output**: `✓ All essential dependencies are installed.` (or installs `gpg` if missing).
- **Potential Issues**: None likely, as `apt-get update` and `install` are robust, and internet is assumed available.
##### **check_system**
- **Logic**: Verifies root privileges, OS compatibility (Ubuntu 22.04), internet connectivity, SSH service, and `/var/log` writability.
- **Simulation**:
- Root check: `id -u` returns 0 (root), passes.
- OS check: `/etc/os-release` confirms `ID=ubuntu`, `VERSION_ID=22.04`, passes.
- Container check: No container detected (`/proc/1/cgroup` lacks docker/lxc/kubepod), so `IS_CONTAINER=false`.
- SSH check: Assumes `openssh-server` is installed (common in Ubuntu server). Detects `ssh.service` or `sshd.service`.
- Internet check: `curl -s --head https://archive.ubuntu.com` succeeds.
- `/var/log` and `/etc/shadow` checks: Permissions are correct (640 for `/etc/shadow`, writable `/var/log`).
- **Expected Output**:
```
✓ Running with root privileges.
✓ Compatible OS detected: Ubuntu 22.04 LTS
✓ Internet connectivity confirmed.
```
- **Potential Issues**: If `openssh-server` is missing, the script installs it later in `install_packages`. No issues expected.
##### **collect_config**
- **Logic**: Prompts for username, hostname, pretty hostname, and SSH port. Validates inputs.
- **Simulation Inputs**:
- Username: `adminuser` (valid, passes `validate_username`).
- Hostname: `myserver` (valid, passes `validate_hostname`).
- Pretty hostname: `My Server` (optional, accepted).
- SSH port: `2222` (default, passes `validate_port`).
- Server IP: Detected via `curl -s https://ifconfig.me` (e.g., `192.0.2.1`).
- Confirmation: User confirms the configuration.
- **Expected Output**:
```
Configuration Summary:
Username: adminuser
Hostname: myserver
SSH Port: 2222
Server IP: 192.0.2.1
Continue with this configuration? [Y/n]: y
```
- **Log Entry**: `Configuration collected: USER=adminuser, HOST=myserver, PORT=2222`
- **Potential Issues**: Invalid inputs (e.g., username with spaces) prompt re-entry, which is robust. No issues expected.
##### **install_packages**
- **Logic**: Updates and upgrades packages, installs essentials (`ufw`, `fail2ban`, `chrony`, `rsync`, etc.).
- **Simulation**:
- `apt-get update` and `apt-get upgrade -y` run silently.
- Installs packages like `ufw`, `fail2ban`, `chrony`, `rsync`, `openssh-server`, etc.
- Assumes sufficient disk space and internet access.
- **Expected Output**: `✓ Essential packages installed.`
- **Potential Issues**: Rare chance of `apt-get` failures due to repository issues, but `set -e` ensures the script exits on error. No issues expected in a fresh VM.
##### **setup_user**
- **Logic**: Creates `adminuser` if it doesnt exist, sets a password (or skips for key-only), adds to `sudo` group, and configures SSH keys.
- **Simulation**:
- User `adminuser` doesnt exist, so `adduser --disabled-password --gecos "" adminuser` runs.
- Password prompt: User enters `securepassword123` twice, set via `chpasswd`.
- SSH key prompt: User pastes a valid key (`ssh-ed25519 AAAAC3Nza... user@local`).
- Key is added to `/home/adminuser/.ssh/authorized_keys` with `chmod 600` and `chown adminuser:adminuser`.
- Adds `adminuser` to `sudo` group with `usermod -aG sudo adminuser`.
- **Expected Output**:
```
✓ User 'adminuser' created.
✓ SSH public key added.
✓ User added to sudo group.
✓ Sudo group membership confirmed for 'adminuser'.
```
- **Potential Issues**: Password mismatch prompts re-entry. If key is invalid, user is prompted again. Robust validation prevents issues.
##### **configure_system**
- **Logic**: Sets timezone, hostname, and optionally configures locales. Backs up `/etc/hosts`, `/etc/fstab`, `/etc/sysctl.conf`.
- **Simulation**:
- Timezone: User enters `America/New_York`, validated via `/usr/share/zoneinfo`.
- Locale configuration: User skips (`dpkg-reconfigure locales` not run).
- Hostname: Sets `myserver` and pretty name `My Server` via `hostnamectl`.
- Updates `/etc/hosts` with `127.0.1.1 myserver`.
- **Expected Output**:
```
✓ Timezone set to America/New_York.
✓ Hostname configured: myserver
```
- **Potential Issues**: Invalid timezone prompts re-entry. No issues expected.
##### **configure_ssh**
- **Logic**: Hardens SSH by setting a custom port (2222), disabling root login, enforcing key-based auth, and creating `/etc/issue.net`. Includes rollback on failure.
- **Simulation**:
- Detects `ssh.service` (Ubuntu 22.04).
- Current port: 22 (default).
- Backs up `/etc/ssh/sshd_config` to `/root/setup_harden_backup_20250630_222800/sshd_config.backup_*`.
- Sets port 2222 in `/etc/ssh/sshd_config` (Ubuntu 22.04 uses direct config).
- Creates `/etc/ssh/sshd_config.d/99-hardening.conf` with:
```
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
ClientAliveInterval 300
X11Forwarding no
PrintMotd no
Banner /etc/issue.net
```
- Creates `/etc/issue.net` with a warning banner.
- Restarts `ssh.service` and verifies port 2222 with `ss -tuln`.
- User tests SSH: `ssh -p 2222 adminuser@192.0.2.1` (assumed successful).
- Verifies root login is disabled with `ssh -p 2222 root@localhost` (fails, as expected).
- **Expected Output**:
```
✓ SSH service restarted on port 2222.
✓ Confirmed: Root SSH login is disabled.
✓ SSH hardening confirmed and finalized.
```
- **Potential Issues**: If the user fails to test SSH on port 2222, the script rolls back to port 22. The `trap` ensures rollback on errors. No issues expected with correct user input.
##### **configure_firewall**
- **Logic**: Configures UFW with deny incoming, allow outgoing, and specific ports (2222/tcp, optional 80/tcp, 443/tcp, 41641/udp).
- **Simulation**:
- UFW is inactive initially.
- Sets `ufw default deny incoming`, `ufw default allow outgoing`.
- Allows `2222/tcp` (SSH).
- User allows HTTP (80/tcp) and HTTPS (443/tcp), skips Tailscale (41641/udp) and custom ports.
- Enables UFW with `ufw --force enable`.
- **Expected Output**:
```
✓ HTTP traffic allowed.
✓ HTTPS traffic allowed.
✓ Firewall is active.
Status: active
To Action From
-- ------ ----
2222/tcp (Custom SSH) ALLOW Anywhere
80/tcp (HTTP) ALLOW Anywhere
443/tcp (HTTPS) ALLOW Anywhere
```
- **Potential Issues**: If the VPS providers firewall blocks port 2222, the user is warned to check. UFW enable failure is caught by `set -e`. No issues expected.
##### **configure_fail2ban**
- **Logic**: Configures Fail2Ban to monitor SSH on port 2222 with `bantime=1h`, `findtime=10m`, `maxretry=3`.
- **Simulation**:
- Creates `/etc/fail2ban/jail.local` with:
```
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
backend = auto
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
backend = %(sshd_backend)s
```
- Enables and restarts `fail2ban`.
- **Expected Output**:
```
✓ Fail2Ban is active and monitoring port(s) 2222.
Status: sshd
```
- **Potential Issues**: Fail2Ban service failure is caught and exits the script. No issues expected.
##### **configure_auto_updates**
- **Logic**: Configures `unattended-upgrades` for automatic security updates.
- **Simulation**:
- User confirms enabling auto-updates.
- Sets `unattended-upgrades/enable_auto_updates` to `true` and runs `dpkg-reconfigure`.
- **Expected Output**: `✓ Automatic security updates enabled.`
- **Potential Issues**: Package is already installed via `install_packages`. No issues expected.
##### **configure_time_sync**
- **Logic**: Enables and verifies `chrony` for time synchronization.
- **Simulation**:
- `systemctl enable --now chrony` runs.
- `chronyc tracking` confirms synchronization.
- **Expected Output**:
```
✓ Chrony is active for time synchronization.
Reference ID : 192.168.1.1 (time.example.com)
Stratum : 2
...
```
- **Potential Issues**: Chrony failure is caught and exits. No issues expected.
##### **install_docker**
- **Logic**: Installs Docker if user confirms, adds `adminuser` to `docker` group, and runs a `hello-world` test.
- **Simulation**:
- User confirms Docker installation.
- Removes old runtimes, adds Docker GPG key and repository, installs `docker-ce`, `docker-ce-cli`, etc.
- Configures `/etc/docker/daemon.json` with log settings.
- Adds `adminuser` to `docker` group.
- Runs `docker run --rm hello-world` as `adminuser`.
- **Expected Output**:
```
✓ Docker sanity check passed.
NOTE: 'adminuser' must log out and back in to use Docker without sudo.
```
- **Potential Issues**: Docker repository issues are caught by `set -e`. No issues expected with internet access.
##### **install_tailscale**
- **Logic**: Installs Tailscale if confirmed, connects using a pre-auth key, and applies optional flags.
- **Simulation**:
- User confirms Tailscale installation.
- Chooses standard Tailscale (option 1).
- Enters key: `tskey-auth-xyz123`.
- Skips additional flags ( `--ssh`, `--advertise-exit-node`, etc.).
- Runs `tailscale up --auth-key=tskey-auth-xyz123 --operator=adminuser`.
- Verifies connection with `tailscale ip` (e.g., `100.64.0.1`).
- **Expected Output**:
```
✓ Tailscale connected successfully. Node IPv4 in tailnet: 100.64.0.1
```
- **Potential Issues**: Invalid key or network issues are logged to `/tmp/tailscale_status.txt`. Retries (3x) mitigate transient failures.
##### **setup_backup**
- **Logic**: Configures rsync backups over SSH with optional notifications and a test backup.
- **Simulation**:
- User confirms backup setup.
- Backup destination: `u12345@u12345.your-storagebox.de`.
- Port: `23` (Hetzner).
- Remote path: `/home/backups/`.
- Hetzner mode: Enabled (uses `-s` for `ssh-copy-id`).
- Key copy: Manual (user runs `ssh-copy-id -p 23 -i /root/.ssh/id_ed25519.pub -s u12345@u12345.your-storagebox.de`).
- Creates `/root/.ssh/id_ed25519` if missing.
- Creates `/root/rsync_exclude.txt` with defaults.
- Cron schedule: `5 3 * * *` (daily at 3:05 AM).
- Notifications: Skipped.
- Test backup: User confirms, creates `/root/test_backup_*`, runs `rsync` to `u12345@u12345.your-storagebox.de:/home/backups/test_backup/`.
- **Expected Output**:
```
✓ Root SSH key generated at /root/.ssh/id_ed25519
ACTION REQUIRED: Copy the root SSH key to the backup destination.
The root user's public key is: ssh-ed25519 AAAAC3Nza... root@myserver
Run the following command: ssh-copy-id -p "23" -i "/root/.ssh/id_ed25519.pub" -s "u12345@u12345.your-storagebox.de"
✓ Rsync exclude file created.
✓ Test backup successful! Check /var/log/backup_rsync.log for details.
✓ Backup cron job scheduled: 5 3 * * *
```
- **Potential Issues**: If the SSH key isnt copied, the test backup fails, logged to `/var/log/backup_rsync.log`. Manual copy instructions are clear. No issues expected with correct setup.
##### **configure_swap**
- **Logic**: Configures a swap file if confirmed, with default size 2G.
- **Simulation**:
- User confirms swap creation.
- Size: `2G`.
- Disk space check: Assumes >2GB available.
- Creates `/swapfile` with `fallocate`, `chmod 600`, `mkswap`, `swapon`.
- Adds `/swapfile none swap sw 0 0` to `/etc/fstab`.
- Sets `vm.swappiness=10`, `vm.vfs_cache_pressure=50` in `/etc/sysctl.d/99-swap.conf`.
- **Expected Output**:
```
✓ Swap file created: 2G
✓ Swap entry added to /etc/fstab.
✓ Swap settings applied to /etc/sysctl.d/99-swap.conf.
NAME TYPE SIZE USED PRIO
/swapfile file 2G 0B -2
```
- **Potential Issues**: Insufficient disk space exits the script. No issues expected.
##### **configure_security_audit**
- **Logic**: Runs Lynis and (on Debian) debsecan if confirmed.
- **Simulation**:
- User confirms audit.
- Installs `lynis`, runs `lynis audit system --quick`.
- Skips debsecan (Ubuntu 22.04, not supported).
- Logs to `/var/log/setup_harden_security_audit_20250630_222800.log`.
- Extracts hardening index (e.g., `75`).
- **Expected Output**:
```
✓ Lynis audit completed. Check /var/log/setup_harden_security_audit_20250630_222800.log for details.
debsecan is not supported on Ubuntu. Skipping debsecan audit.
```
- **Potential Issues**: Lynis failure is logged and doesnt exit the script. No issues expected.
##### **final_cleanup**
- **Logic**: Runs `apt-get update`, `upgrade`, `autoremove`, `autoclean`, and reloads daemons.
- **Simulation**: All commands succeed.
- **Expected Output**: `✓ Final system update and cleanup complete.`
- **Potential Issues**: Repository issues are logged as warnings, not fatal. No issues expected.
##### **generate_summary**
- **Logic**: Summarizes configuration, checks service status, and provides verification steps.
- **Simulation**:
- Services (`ssh.service`, `fail2ban`, `chrony`, `ufw`, `docker`, `tailscaled`) are active.
- Backup is configured, test successful.
- Tailscale is connected (IP: `100.64.0.1`).
- Audit ran, hardening index: `75`, debsecan: `Not supported on Ubuntu`.
- **Expected Output**:
```
Setup Complete!
✓ Service ssh.service is active.
✓ Service fail2ban is active.
✓ Service chrony is active.
✓ Service ufw is active.
✓ Service docker is active.
✓ Service tailscaled is active and connected.
✓ Security audit performed.
Configuration Summary:
Admin User: adminuser
Hostname: myserver
SSH Port: 2222
Server IP: 192.0.2.1
Remote Backup: Enabled
- Backup Script: /root/run_backup.sh
- Destination: u12345@u12345.your-storagebox.de
- SSH Port: 23
- Remote Path: /home/backups/
- Cron Schedule: 5 3 * * *
- Notifications: None
- Test Status: Successful
Tailscale: Enabled
- Server: https://controlplane.tailscale.com
- Tailscale IPs: 100.64.0.1
- Flags: None
Security Audit: Performed
- Audit Log: /var/log/setup_harden_security_audit_20250630_222800.log
- Hardening Index: 75
- Vulnerabilities: Not supported on Ubuntu
Log File: /var/log/du_setup_20250630_222800.log
Backups: /root/setup_harden_backup_20250630_222800
Post-Reboot Verification Steps:
- SSH access: ssh -p 2222 adminuser@192.0.2.1
- Firewall rules: sudo ufw status verbose
- Time sync: chronyc tracking
- Fail2Ban status: sudo fail2ban-client status sshd
- Swap status: sudo swapon --show && free -h
- Hostname: hostnamectl
- Docker status: docker ps
- Tailscale status: tailscale status
- Remote Backup:
- Verify SSH key: sudo cat /root/.ssh/id_ed25519.pub
- Copy key if needed: ssh-copy-id -p 23 -s u12345@u12345.your-storagebox.de
- Test backup: sudo /root/run_backup.sh
- Check logs: sudo less /var/log/backup_rsync.log
- Security Audit:
- Check results: sudo less /var/log/setup_harden_security_audit_20250630_222800.log
⚠ ACTION REQUIRED: Ensure the root SSH key (/root/.ssh/id_ed25519.pub) is copied to u12345@u12345.your-storagebox.de.
⚠ A reboot is required to apply all changes cleanly.
Reboot now? [Y/n]: n
⚠ Please reboot manually with 'sudo reboot'.
```
- **Potential Issues**: If services fail, theyre listed in `FAILED_SERVICES`. Backup key copy warning for manual mode. No issues expected.
---
### **Potential Runtime Issues**
- **SSH Lockout**: If the user fails to test SSH on port 2222, the script rolls back to port 22, preventing lockout. The warning to test in a separate terminal is clear.
- **Backup Failure**: If the root SSH key isnt copied to the backup server, the test backup fails, and logs provide clear troubleshooting steps (e.g., `ssh-copy-id`, `nc -zv`).
- **Tailscale**: Invalid keys or network issues (e.g., UDP 41641 blocked) are caught with retries and logged to `/tmp/tailscale_status.txt`.
- **Disk Space**: Swap creation checks available space, exiting if insufficient. Assumed 2GB available in the VM.
- **Package Installation**: Repository failures are caught by `set -e`, and the script exits cleanly.