- **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`.
- **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 doesn’t exist, sets a password (or skips for key-only), adds to `sudo` group, and configures SSH keys.
- **Simulation**:
- User `adminuser` doesn’t 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).
- 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).
- 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 provider’s 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`.
- 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 isn’t 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 doesn’t exit the script. No issues expected.
- **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 isn’t 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.