mirror of
https://github.com/buildplan/du_setup.git
synced 2025-12-29 16:14:59 +00:00
option to run Lynis and debsecan, updated to ufw, tailscale and summary
This commit is contained in:
61
README.md
61
README.md
@@ -1,6 +1,6 @@
|
||||
# Debian & Ubuntu Server Setup & Hardening Script
|
||||
|
||||
**Version:** 4.1
|
||||
**Version:** 4.2
|
||||
|
||||
**Last Updated:** 2025-06-29
|
||||
|
||||
@@ -22,7 +22,9 @@ This script automates the initial setup and security hardening of a fresh Debian
|
||||
- **Automated Security Updates**: Enables `unattended-upgrades` for automatic security patches.
|
||||
- **System Stability**: Configures NTP time synchronization with `chrony` and optional swap file setup for low-RAM systems.
|
||||
- **Remote rsync Backups**: Configures automated `rsync` backups over SSH to any compatible server (e.g., Hetzner Storage Box), with SSH key automation (`sshpass` or manual), cron scheduling, ntfy/Discord notifications, and a customizable exclude file.
|
||||
- **Backup Testing**: Includes an optional test backup to verify the rsync configuration before scheduling.
|
||||
- **Tailscale VPN**: Installs Tailscale and connects to the standard Tailscale network (pre-auth key required) or a custom server (URL and key required). Configures optional flags (`--ssh`, `--advertise-exit-node`, `--accept-dns`, `--accept-routes`).
|
||||
- **Security Auditing**: Optionally runs **Lynis** for system hardening audits and **debsecan** for package vulnerability checks, with results logged for review.
|
||||
- **Safety First**: Backs up critical configuration files before modification, stored in `/root/setup_harden_backup_*`.
|
||||
- **Optional Software**: Offers interactive installation of:
|
||||
- Docker & Docker Compose
|
||||
@@ -37,25 +39,26 @@ This script automates the initial setup and security hardening of a fresh Debian
|
||||
- Fresh installation of a compatible OS.
|
||||
- Root or `sudo` privileges.
|
||||
- Internet access for package downloads.
|
||||
- For remote backups: An SSH-accessible server (e.g., Hetzner Storage Box) with credentials or SSH key access.
|
||||
- Minimum 2GB disk space for swap file creation and temporary files.
|
||||
- For remote backups: An SSH-accessible server (e.g., Hetzner Storage Box) with credentials or SSH key access. For Hetzner, SSH (port 23) is used for rsync.
|
||||
- For Tailscale: A pre-auth key from https://login.tailscale.com/admin (standard, starts with `tskey-auth-`) or from a custom server (e.g., `https://ts.mydomain.cloud`).
|
||||
|
||||
### 1. Download the Script
|
||||
|
||||
```
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/buildplan/setup_harden_server/refs/heads/main/setup_harden_debian_ubuntu.sh
|
||||
chmod +x setup_harden_debian_ubuntu.sh
|
||||
```
|
||||
|
||||
### 2. Run Interactively (Recommended)
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo ./setup_harden_debian_ubuntu.sh
|
||||
```
|
||||
|
||||
### 3. Run in Quiet Mode (for Automation)
|
||||
|
||||
```
|
||||
```bash
|
||||
sudo ./setup_harden_debian_ubuntu.sh --quiet
|
||||
```
|
||||
|
||||
@@ -68,11 +71,13 @@ sudo ./setup_harden_debian_ubuntu.sh --quiet
|
||||
| Task | Description |
|
||||
| --- | --- |
|
||||
| **System Checks** | Verifies OS compatibility, root privileges, and internet connectivity. |
|
||||
| **Package Management** | Updates packages and installs tools (`ufw`, `fail2ban`, `chrony`, `rsync`, etc.). |
|
||||
| **Package Management** | Updates packages and installs tools (`ufw`, `fail2ban`, `chrony`, `rsync`, `lynis`, `debsecan`, etc.). |
|
||||
| **Admin User Creation** | Creates a `sudo` user with a password and/or SSH public key. |
|
||||
| **SSH Hardening** | Disables root login, enforces key-based auth, and sets a custom port. |
|
||||
| **Firewall Setup** | Configures UFW to deny incoming traffic by default, allowing specific ports. |
|
||||
| **Remote Backup Setup** | Configures `rsync` backups to an SSH server (e.g., `u457300-sub4@u457300.your-storagebox.de:23`). Creates `/root/run_backup.sh`, `/root/rsync_exclude.txt`, and schedules a cron job. Supports ntfy/Discord notifications. |
|
||||
| **Backup Testing** | Performs an optional test backup to verify rsync configuration, logging results to `/var/log/backup_rsync.log`. |
|
||||
| **Security Auditing** | Runs optional **Lynis** and **debsecan** audits, logging results to `/var/log/setup_harden_security_audit_*.log`. |
|
||||
| **Tailscale Setup** | Installs Tailscale and connects to the standard Tailscale network (pre-auth key starting with `tskey-auth-`) or a custom server (any valid key). Configures optional flags (`--ssh`, `--advertise-exit-node`, `--accept-dns`, `--accept-routes`). |
|
||||
| **System Backups** | Saves timestamped configuration backups in `/root/setup_harden_backup_*`. |
|
||||
| **Swap File Setup** | Creates an optional swap file (e.g., 2G) with tuned settings. |
|
||||
@@ -84,6 +89,7 @@ sudo ./setup_harden_debian_ubuntu.sh --quiet
|
||||
|
||||
- **Log Files**: `/var/log/setup_harden_debian_ubuntu_*.log`
|
||||
- **Backup Logs**: `/var/log/backup_rsync.log` (for remote backup operations)
|
||||
- **Audit Logs**: `/var/log/setup_harden_security_audit_*.log` (for Lynis and debsecan results)
|
||||
- **Configuration Backups**: `/root/setup_harden_backup_*`
|
||||
|
||||
## Post-Reboot Verification
|
||||
@@ -108,24 +114,28 @@ After rebooting, verify the setup:
|
||||
- Copy key (if not done): `ssh-copy-id -p <backup_port> -s <backup_user@backup_host>`
|
||||
- Test backup: `sudo /root/run_backup.sh`
|
||||
- Check logs: `sudo less /var/log/backup_rsync.log`
|
||||
- Verify cron job: `sudo crontab -l` (e.g., `3 3 * * * /root/run_backup.sh`)
|
||||
- Verify cron job: `sudo crontab -l` (e.g., `5 3 * * * /root/run_backup.sh`)
|
||||
- **Security Audit** (if run):
|
||||
- Check results: `sudo less /var/log/setup_harden_security_audit_*.log`
|
||||
- Review Lynis hardening index and debsecan vulnerabilities in the script’s summary output
|
||||
|
||||
## Tested On
|
||||
|
||||
- Debian 12
|
||||
- Ubuntu 22.04, 24.04, 24.10 (experimental)
|
||||
- Cloud providers: DigitalOcean, Oracle Cloud, Hetzner, Netcup
|
||||
- Backup destinations: Hetzner Storage Box, custom SSH servers
|
||||
- Backup destinations: Hetzner Storage Box (SSH, port 23), custom SSH servers
|
||||
- Tailscale: Standard network, custom self-hosted servers
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Run on a fresh system**: Designed for initial provisioning.
|
||||
- **Run on a fresh system**: Designed for initial provisioning with at least 2GB free disk space.
|
||||
- **Reboot required**: Ensures kernel and service changes apply cleanly.
|
||||
- Test in a non-production environment (e.g., staging VM) first.
|
||||
- Maintain out-of-band console access in case of SSH lockout.
|
||||
- For Hetzner Storage Box, ensure `~/.ssh/` exists on the remote server: `ssh -p 23 <backup_user@backup_host> "mkdir -p ~/.ssh && chmod 700 ~/.ssh"`.
|
||||
- For Hetzner Storage Box, ensure `~/.ssh/` exists on the remote server: `ssh -p 23 <backup_user@backup_host> "mkdir -p ~/.ssh && chmod 700 ~/.ssh"`. Backups use SSH (port 23) for rsync, not SFTP.
|
||||
- For Tailscale, generate a pre-auth key from https://login.tailscale.com/admin (standard, must start with `tskey-auth-`) or your custom server (any valid key). Ensure UDP 41641 is open for Tailscale traffic.
|
||||
- For security audits, review `/var/log/setup_harden_security_audit_*.log` for Lynis and debsecan recommendations.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -134,18 +144,18 @@ After rebooting, verify the setup:
|
||||
If locked out, use your provider’s console:
|
||||
|
||||
1. **Remove Hardened Configuration**:
|
||||
```
|
||||
```bash
|
||||
rm /etc/ssh/sshd_config.d/99-hardening.conf
|
||||
```
|
||||
|
||||
2. **Restore Original `sshd_config`**:
|
||||
```
|
||||
```bash
|
||||
LATEST_BACKUP=$(ls -td /root/setup_harden_backup_* | head -1)
|
||||
cp "$LATEST_BACKUP"/sshd_config.backup_* /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
3. **Restart SSH**:
|
||||
```
|
||||
```bash
|
||||
systemctl restart ssh
|
||||
```
|
||||
|
||||
@@ -164,14 +174,14 @@ If backups fail:
|
||||
- If automated key copy fails: `cat /tmp/ssh-copy-id.log`
|
||||
|
||||
3. **Test Backup Manually**:
|
||||
```
|
||||
```bash
|
||||
sudo /root/run_backup.sh
|
||||
```
|
||||
|
||||
4. **Verify Cron Job**:
|
||||
- Check: `sudo crontab -l`
|
||||
- Ensure: `3 3 * * * /root/run_backup.sh #-*- managed by setup_harden script -*-`
|
||||
- Test cron permissions: `echo "3 3 * * * /root/run_backup.sh" | crontab -u root -`
|
||||
- Ensure: `5 3 * * * /root/run_backup.sh #-*- managed by setup_harden script -*-`
|
||||
- Test cron permissions: `echo "5 3 * * * /root/run_backup.sh" | crontab -u root -`
|
||||
- Check permissions: `ls -l /var/spool/cron/crontabs/root` (expect `-rw------- root:crontab`)
|
||||
|
||||
5. **Network Issues**:
|
||||
@@ -181,6 +191,23 @@ If backups fail:
|
||||
6. **Summary Errors**:
|
||||
- If summary shows `Remote Backup: Not configured`, verify: `ls -l /root/run_backup.sh`
|
||||
|
||||
### Security Audit Issues
|
||||
|
||||
If audits fail:
|
||||
|
||||
1. **Check Audit Log**:
|
||||
- Review: `sudo less /var/log/setup_harden_security_audit_*.log`
|
||||
- Look for Lynis errors or debsecan CVE reports
|
||||
|
||||
2. **Verify Installation**:
|
||||
- Lynis: `command -v lynis`
|
||||
- Debsecan: `command -v debsecan`
|
||||
- Reinstall if needed: `sudo apt-get install lynis debsecan`
|
||||
|
||||
3. **Run Manually**:
|
||||
- Lynis: `sudo lynis audit system --quick`
|
||||
- Debsecan: `sudo debsecan --suite $(source /etc/os-release && echo $VERSION_CODENAME)`
|
||||
|
||||
### Tailscale Issues
|
||||
|
||||
If Tailscale fails to connect:
|
||||
@@ -210,4 +237,4 @@ If Tailscale fails to connect:
|
||||
|
||||
## [MIT](https://github.com/buildplan/setup_harden_server/blob/main/LICENSE) License
|
||||
|
||||
This script is open-source and provided "as is" without warranty. Use at your own risk.
|
||||
This script is open-source and provided "as is" without warranty. Use at your own risk.
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Debian 12 and Ubuntu Server Hardening Interactive Script
|
||||
# Version: 4.1 | 2025-06-29
|
||||
# Version: 4.2 | 2025-06-29
|
||||
# Changelog:
|
||||
# - v4.2: Added Security Audit Tools (Integrating Lynis and Optionally Debsecan) & option to do Backup Testing
|
||||
# Fixed debsecan compatibility (Debian-only), added global BACKUP_LOG, added backup testing
|
||||
# - v4.1: Added tailscale config to connect to tailscale or headscale server
|
||||
# - v4.0: Added automated backup config. Mainly for Hetzner Storage Box but can be used for any rsync/SSH enabled remote solution.
|
||||
# - v3.*: Improvements to script flow and fixed bugs which were found in tests at Oracle Cloud
|
||||
@@ -56,6 +58,7 @@ NC='\033[0m' # No Color
|
||||
# Script variables
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
LOG_FILE="/var/log/setup_harden_debian_ubuntu_$(date +%Y%m%d_%H%M%S).log"
|
||||
BACKUP_LOG="/var/log/backup_rsync.log"
|
||||
VERBOSE=true
|
||||
BACKUP_DIR="/root/setup_harden_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
IS_CONTAINER=false
|
||||
@@ -63,6 +66,7 @@ SSHD_BACKUP_FILE=""
|
||||
LOCAL_KEY_ADDED=false
|
||||
SSH_SERVICE=""
|
||||
ID="" # This will be populated from /etc/os-release
|
||||
FAILED_SERVICES=()
|
||||
|
||||
# --- PARSE ARGUMENTS ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -83,7 +87,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}║ v4.1 | 2025-06-29 ║${NC}"
|
||||
echo -e "${CYAN}║ v4.2 | 2025-06-29 ║${NC}"
|
||||
echo -e "${CYAN}║ ║${NC}"
|
||||
echo -e "${CYAN}╚═════════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo
|
||||
@@ -730,6 +734,15 @@ configure_firewall() {
|
||||
print_info "HTTPS rule already exists."
|
||||
fi
|
||||
fi
|
||||
if confirm "Allow Tailscale traffic (UDP 41641)?"; then
|
||||
if ! ufw status | grep -qw "41641/udp"; then
|
||||
ufw allow 41641/udp comment 'Tailscale VPN'
|
||||
print_success "Tailscale traffic (UDP 41641) allowed."
|
||||
log "Added UFW rule for Tailscale (41641/udp)."
|
||||
else
|
||||
print_info "Tailscale rule (UDP 41641) already exists."
|
||||
fi
|
||||
fi
|
||||
if confirm "Add additional custom ports (e.g., 8080/tcp, 123/udp)?"; then
|
||||
while true; do
|
||||
local CUSTOM_PORTS # Make variable local to the loop
|
||||
@@ -751,9 +764,16 @@ configure_firewall() {
|
||||
if ufw status | grep -qw "$port"; then
|
||||
print_info "Rule for $port already exists."
|
||||
else
|
||||
ufw allow "$port" comment "Custom port $port"
|
||||
print_success "Added rule for $port."
|
||||
log "Added UFW rule for $port."
|
||||
local CUSTOM_COMMENT
|
||||
read -rp "$(echo -e "${CYAN}Enter comment for $port (e.g., 'My App Port'): ${NC}")" CUSTOM_COMMENT
|
||||
if [[ -z "$CUSTOM_COMMENT" ]]; then
|
||||
CUSTOM_COMMENT="Custom port $port"
|
||||
fi
|
||||
# Sanitize comment to avoid breaking UFW command
|
||||
CUSTOM_COMMENT=$(echo "$CUSTOM_COMMENT" | tr -d "'\"\\")
|
||||
ufw allow "$port" comment "$CUSTOM_COMMENT"
|
||||
print_success "Added rule for $port with comment '$CUSTOM_COMMENT'."
|
||||
log "Added UFW rule for $port with comment '$CUSTOM_COMMENT'."
|
||||
fi
|
||||
done
|
||||
break
|
||||
@@ -773,7 +793,7 @@ configure_firewall() {
|
||||
print_error "UFW failed to activate. Check 'journalctl -u ufw' for details."
|
||||
exit 1
|
||||
fi
|
||||
print_warning "ACTION REQUIRED: Check your VPS provider's edge firewall to allow opened ports (e.g., $SSH_PORT/tcp)."
|
||||
print_warning "ACTION REQUIRED: Check your VPS provider's edge firewall to allow opened ports (e.g., $SSH_PORT/tcp, 41641/udp for Tailscale)."
|
||||
ufw status verbose | tee -a "$LOG_FILE"
|
||||
log "Firewall configuration completed."
|
||||
}
|
||||
@@ -831,6 +851,10 @@ configure_auto_updates() {
|
||||
print_error "unattended-upgrades package is not installed."
|
||||
exit 1
|
||||
fi
|
||||
# Check for existing unattended-upgrades configuration
|
||||
if [[ -f /etc/apt/apt.conf.d/50unattended-upgrades ]] && grep -q "Unattended-Upgrade::Allowed-Origins" /etc/apt/apt.conf.d/50unattended-upgrades; then
|
||||
print_info "Existing unattended-upgrades configuration found. Verify with 'cat /etc/apt/apt.conf.d/50unattended-upgrades'."
|
||||
fi
|
||||
print_info "Configuring unattended upgrades..."
|
||||
echo "unattended-upgrades unattended-upgrades/enable_auto_updates boolean true" | debconf-set-selections
|
||||
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure -f noninteractive unattended-upgrades
|
||||
@@ -911,8 +935,12 @@ install_tailscale() {
|
||||
print_section "Tailscale VPN Installation and Configuration"
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
print_info "Tailscale already installed."
|
||||
if systemctl is-active --quiet tailscaled && tailscale status >/dev/null 2>&1; then
|
||||
print_success "Tailscale service is active and connected."
|
||||
if systemctl is-active --quiet tailscaled && tailscale ip >/dev/null 2>&1; then
|
||||
local TS_IPS TS_IPV4
|
||||
TS_IPS=$(tailscale ip 2>/dev/null || echo "Unknown")
|
||||
TS_IPV4=$(echo "$TS_IPS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "Unknown")
|
||||
print_success "Tailscale service is active and connected. Node IPv4 in tailnet: $TS_IPV4"
|
||||
echo "$TS_IPS" > /tmp/tailscale_ips.txt
|
||||
return 0
|
||||
else
|
||||
print_warning "Tailscale installed but service is not active or not connected."
|
||||
@@ -939,8 +967,12 @@ install_tailscale() {
|
||||
fi
|
||||
|
||||
# --- Configure Tailscale Connection ---
|
||||
if systemctl is-active --quiet tailscaled && tailscale status >/dev/null 2>&1 && tailscale status | grep -q "^[^ ]* \+${SERVER_NAME} \+"; then
|
||||
print_info "Tailscale is already connected (hostname: $SERVER_NAME)."
|
||||
if systemctl is-active --quiet tailscaled && tailscale ip >/dev/null 2>&1; then
|
||||
local TS_IPS TS_IPV4
|
||||
TS_IPS=$(tailscale ip 2>/dev/null || echo "Unknown")
|
||||
TS_IPV4=$(echo "$TS_IPS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "Unknown")
|
||||
print_info "Tailscale is already connected. Node IPv4 in tailnet: $TS_IPV4"
|
||||
echo "$TS_IPS" > /tmp/tailscale_ips.txt
|
||||
return 0
|
||||
fi
|
||||
print_info "Configuring Tailscale connection..."
|
||||
@@ -982,23 +1014,29 @@ install_tailscale() {
|
||||
local RETRIES=3
|
||||
local DELAY=5
|
||||
local CONNECTED=false
|
||||
local TS_IPS TS_IPV4
|
||||
for ((i=1; i<=RETRIES; i++)); do
|
||||
if tailscale status 2>/dev/null | grep -q "^[^ ]* \+${SERVER_NAME} \+.*[^offline]$"; then
|
||||
CONNECTED=true
|
||||
break
|
||||
if tailscale ip >/dev/null 2>&1; then
|
||||
TS_IPS=$(tailscale ip 2>/dev/null || echo "Unknown")
|
||||
TS_IPV4=$(echo "$TS_IPS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "Unknown")
|
||||
if [[ -n "$TS_IPV4" && "$TS_IPV4" != "Unknown" ]]; then
|
||||
CONNECTED=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
print_info "Waiting for Tailscale to connect ($i/$RETRIES)..."
|
||||
sleep $DELAY
|
||||
done
|
||||
if $CONNECTED; then
|
||||
print_success "Tailscale connected successfully (hostname: $SERVER_NAME)."
|
||||
print_success "Tailscale connected successfully. Node IPv4 in tailnet: $TS_IPV4"
|
||||
log "Tailscale connected: $TS_COMMAND"
|
||||
# Store connection details for summary
|
||||
echo "${LOGIN_SERVER:-https://controlplane.tailscale.com}" > /tmp/tailscale_server
|
||||
echo "$TS_IPS" > /tmp/tailscale_ips.txt
|
||||
echo "None" > /tmp/tailscale_flags
|
||||
else
|
||||
print_warning "Tailscale connection attempt succeeded, but hostname ($SERVER_NAME) not found in 'tailscale status'."
|
||||
print_info "Please verify with 'tailscale status' and run the following command manually if needed:"
|
||||
print_warning "Tailscale connection attempt succeeded, but no IPs assigned."
|
||||
print_info "Please verify with 'tailscale ip' and run the following command manually if needed:"
|
||||
echo -e "${CYAN} $TS_COMMAND${NC}"
|
||||
log "Tailscale connection not verified: $TS_COMMAND"
|
||||
tailscale status > /tmp/tailscale_status.txt 2>&1
|
||||
@@ -1045,22 +1083,28 @@ install_tailscale() {
|
||||
local RETRIES=3
|
||||
local DELAY=5
|
||||
local CONNECTED=false
|
||||
local TS_IPS TS_IPV4
|
||||
for ((i=1; i<=RETRIES; i++)); do
|
||||
if tailscale status 2>/dev/null | grep -q "^[^ ]* \+${SERVER_NAME} \+.*[^offline]$"; then
|
||||
CONNECTED=true
|
||||
break
|
||||
if tailscale ip >/dev/null 2>&1; then
|
||||
TS_IPS=$(tailscale ip 2>/dev/null || echo "Unknown")
|
||||
TS_IPV4=$(echo "$TS_IPS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "Unknown")
|
||||
if [[ -n "$TS_IPV4" && "$TS_IPV4" != "Unknown" ]]; then
|
||||
CONNECTED=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
print_info "Waiting for Tailscale to connect ($i/$RETRIES)..."
|
||||
sleep $DELAY
|
||||
done
|
||||
if $CONNECTED; then
|
||||
print_success "Tailscale reconfigured with additional options."
|
||||
print_success "Tailscale reconfigured with additional options. Node IPv4 in tailnet: $TS_IPV4"
|
||||
log "Tailscale reconfigured: $TS_COMMAND"
|
||||
# Store flags for summary
|
||||
# Store flags and IPs for summary
|
||||
echo "${TS_FLAGS// --/}" > /tmp/tailscale_flags
|
||||
echo "$TS_IPS" > /tmp/tailscale_ips.txt
|
||||
else
|
||||
print_warning "Tailscale reconfiguration attempt succeeded, but hostname ($SERVER_NAME) not found in 'tailscale status'."
|
||||
print_info "Please verify with 'tailscale status' and run the following command manually if needed:"
|
||||
print_warning "Tailscale reconfiguration attempt succeeded, but no IPs assigned."
|
||||
print_info "Please verify with 'tailscale ip' and run the following command manually if needed:"
|
||||
echo -e "${CYAN} $TS_COMMAND${NC}"
|
||||
log "Tailscale reconfiguration not verified: $TS_COMMAND"
|
||||
tailscale status > /tmp/tailscale_status.txt 2>&1
|
||||
@@ -1076,7 +1120,7 @@ install_tailscale() {
|
||||
log "No additional Tailscale options applied."
|
||||
fi
|
||||
print_success "Tailscale setup complete."
|
||||
print_info "Verify status: tailscale status"
|
||||
print_info "Verify status: tailscale ip"
|
||||
log "Tailscale setup completed."
|
||||
}
|
||||
|
||||
@@ -1326,6 +1370,9 @@ EOF
|
||||
fi
|
||||
print_success "Backup script created."
|
||||
|
||||
# --- Backup test ---
|
||||
test_backup
|
||||
|
||||
# --- Configure Cron Job ---
|
||||
print_info "Configuring root cron job..."
|
||||
# Ensure crontab is writable
|
||||
@@ -1363,12 +1410,73 @@ EOF
|
||||
log "Backup configuration completed."
|
||||
}
|
||||
|
||||
test_backup() {
|
||||
print_section "Backup Configuration Test"
|
||||
if [[ ! -f /root/run_backup.sh ]]; then
|
||||
print_error "Backup script not found. Cannot run test."
|
||||
log "Backup test failed: /root/run_backup.sh not found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! confirm "Run a test backup to verify configuration?"; then
|
||||
print_info "Skipping backup test."
|
||||
log "Backup test skipped by user."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local BACKUP_DEST=$(grep "^REMOTE_DEST=" /root/run_backup.sh | cut -d'"' -f2 || echo "unknown")
|
||||
local BACKUP_PORT=$(grep "^SSH_PORT=" /root/run_backup.sh | cut -d'"' -f2 || echo "22")
|
||||
local REMOTE_BACKUP_PATH=$(grep "^REMOTE_PATH=" /root/run_backup.sh | cut -d'"' -f2 || echo "unknown")
|
||||
local BACKUP_LOG="/var/log/backup_rsync.log"
|
||||
|
||||
if [[ "$BACKUP_DEST" == "unknown" || "$REMOTE_BACKUP_PATH" == "unknown" ]]; then
|
||||
print_error "Invalid backup configuration in /root/run_backup.sh."
|
||||
log "Backup test failed: Invalid configuration in /root/run_backup.sh."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create a temporary test file
|
||||
local TEST_DIR="/root/test_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$TEST_DIR"
|
||||
echo "Test file for backup verification" > "$TEST_DIR/test.txt"
|
||||
chmod 600 "$TEST_DIR/test.txt"
|
||||
|
||||
print_info "Running test backup to $BACKUP_DEST:$REMOTE_BACKUP_PATH..."
|
||||
local RSYNC_OUTPUT
|
||||
RSYNC_OUTPUT=$(rsync -avz --delete -e "ssh -p $BACKUP_PORT" "$TEST_DIR/" "${BACKUP_DEST}:${REMOTE_BACKUP_PATH}test_backup/" 2>&1)
|
||||
local RSYNC_EXIT_CODE=$?
|
||||
echo "--- Test Backup at $(date) ---" >> "$BACKUP_LOG"
|
||||
echo "$RSYNC_OUTPUT" >> "$BACKUP_LOG"
|
||||
|
||||
if [[ $RSYNC_EXIT_CODE -eq 0 ]]; then
|
||||
echo "Test backup successful" >> "$BACKUP_LOG"
|
||||
print_success "Test backup successful! Check $BACKUP_LOG for details."
|
||||
log "Test backup successful."
|
||||
else
|
||||
print_error "Test backup failed (exit code: $RSYNC_EXIT_CODE). Check $BACKUP_LOG for details."
|
||||
print_info "Troubleshooting steps:"
|
||||
print_info " - Verify SSH key: cat /root/.ssh/id_ed25519.pub"
|
||||
print_info " - Copy key: ssh-copy-id -p \"$BACKUP_PORT\" -i /root/.ssh/id_ed25519.pub \"$BACKUP_DEST\""
|
||||
print_info " - Test SSH: ssh -p \"$BACKUP_PORT\" \"$BACKUP_DEST\" true"
|
||||
log "Test backup failed with exit code $RSYNC_EXIT_CODE."
|
||||
fi
|
||||
|
||||
# Clean up test directory
|
||||
rm -rf "$TEST_DIR"
|
||||
print_success "Backup test completed."
|
||||
log "Backup test completed."
|
||||
}
|
||||
|
||||
configure_swap() {
|
||||
if [[ $IS_CONTAINER == true ]]; then
|
||||
print_info "Swap configuration skipped in container."
|
||||
return 0
|
||||
fi
|
||||
print_section "Swap Configuration"
|
||||
# Check for existing swap partition
|
||||
if lsblk -r | grep -q '\[SWAP\]'; then
|
||||
print_warning "Existing swap partition found. Verify with 'lsblk -f'. Proceed with caution."
|
||||
fi
|
||||
local existing_swap
|
||||
existing_swap=$(swapon --show --noheadings | awk '{print $1}' || true)
|
||||
if [[ -n "$existing_swap" ]]; then
|
||||
@@ -1522,6 +1630,79 @@ configure_time_sync() {
|
||||
log "Time synchronization completed."
|
||||
}
|
||||
|
||||
configure_security_audit() {
|
||||
print_section "Security Audit Configuration"
|
||||
if ! confirm "Run a security audit with Lynis (and optionally debsecan on Debian)?"; then
|
||||
print_info "Security audit skipped."
|
||||
log "Security audit skipped by user."
|
||||
AUDIT_RAN=false
|
||||
return 0
|
||||
fi
|
||||
|
||||
AUDIT_LOG="/var/log/setup_harden_security_audit_$(date +%Y%m%d_%H%M%S).log"
|
||||
touch "$AUDIT_LOG" && chmod 600 "$AUDIT_LOG"
|
||||
AUDIT_RAN=true
|
||||
HARDENING_INDEX=""
|
||||
DEBSECAN_VULNS="Not run"
|
||||
|
||||
# Install and run Lynis
|
||||
print_info "Installing Lynis..."
|
||||
if ! apt-get update -qq; then
|
||||
print_error "Failed to update package lists. Cannot install Lynis."
|
||||
log "apt-get update failed for Lynis installation."
|
||||
return 1
|
||||
elif ! apt-get install -y -qq lynis; then
|
||||
print_warning "Failed to install Lynis. Skipping Lynis audit."
|
||||
log "Lynis installation failed."
|
||||
else
|
||||
print_info "Running Lynis audit (non-interactive mode)..."
|
||||
if lynis audit system --quick >> "$AUDIT_LOG" 2>&1; then
|
||||
print_success "Lynis audit completed. Check $AUDIT_LOG for details."
|
||||
log "Lynis audit completed successfully."
|
||||
# Extract hardening index
|
||||
HARDENING_INDEX=$(grep -oP "Hardening index : \K\d+" "$AUDIT_LOG" || echo "Unknown")
|
||||
# Append Lynis system log for persistence
|
||||
cat /var/log/lynis.log >> "$AUDIT_LOG" 2>/dev/null
|
||||
else
|
||||
print_error "Lynis audit failed. Check $AUDIT_LOG for details."
|
||||
log "Lynis audit failed."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if system is Debian before running debsecan
|
||||
source /etc/os-release
|
||||
if [[ "$ID" == "debian" ]]; then
|
||||
if confirm "Also run debsecan to check for package vulnerabilities?"; then
|
||||
print_info "Installing debsecan..."
|
||||
if ! apt-get install -y -qq debsecan; then
|
||||
print_warning "Failed to install debsecan. Skipping debsecan audit."
|
||||
log "debsecan installation failed."
|
||||
else
|
||||
print_info "Running debsecan audit..."
|
||||
if debsecan --suite "$VERSION_CODENAME" >> "$AUDIT_LOG" 2>&1; then
|
||||
DEBSECAN_VULNS=$(grep -c "CVE-" "$AUDIT_LOG" || echo "0")
|
||||
print_success "debsecan audit completed. Found $DEBSECAN_VULNS vulnerabilities."
|
||||
log "debsecan audit completed with $DEBSECAN_VULNS vulnerabilities."
|
||||
else
|
||||
print_error "debsecan audit failed. Check $AUDIT_LOG for details."
|
||||
log "debsecan audit failed."
|
||||
DEBSECAN_VULNS="Failed"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
print_info "debsecan audit skipped."
|
||||
log "debsecan audit skipped by user."
|
||||
fi
|
||||
else
|
||||
print_info "debsecan is not supported on Ubuntu. Skipping debsecan audit."
|
||||
log "debsecan audit skipped (Ubuntu detected)."
|
||||
DEBSECAN_VULNS="Not supported on Ubuntu"
|
||||
fi
|
||||
|
||||
print_warning "Review audit results in $AUDIT_LOG for security recommendations."
|
||||
log "Security audit configuration completed."
|
||||
}
|
||||
|
||||
final_cleanup() {
|
||||
print_section "Final System Cleanup"
|
||||
print_info "Running final system update and cleanup..."
|
||||
@@ -1544,34 +1725,43 @@ generate_summary() {
|
||||
print_success "Service $service is active."
|
||||
else
|
||||
print_error "Service $service is NOT active."
|
||||
FAILED_SERVICES+=("$service")
|
||||
fi
|
||||
done
|
||||
if ufw status | grep -q "Status: active"; then
|
||||
print_success "Service ufw is active."
|
||||
else
|
||||
print_error "Service ufw is NOT active."
|
||||
FAILED_SERVICES+=("ufw")
|
||||
fi
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
if systemctl is-active --quiet docker; then
|
||||
print_success "Service docker is active."
|
||||
else
|
||||
print_error "Service docker is NOT active."
|
||||
FAILED_SERVICES+=("docker")
|
||||
fi
|
||||
fi
|
||||
local TS_COMMAND=""
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
if systemctl is-active --quiet tailscaled && tailscale status >/dev/null 2>&1 && tailscale status | grep -q "^[^ ]* \+${SERVER_NAME} \+.*[^offline]$"; then
|
||||
print_success "Service tailscaled is active and connected."
|
||||
local TS_SERVER=$(cat /tmp/tailscale_server 2>/dev/null || echo "https://controlplane.tailscale.com")
|
||||
local TS_FLAGS=$(cat /tmp/tailscale_flags 2>/dev/null || echo "None")
|
||||
if systemctl is-active --quiet tailscaled && tailscale ip >/dev/null 2>&1; then
|
||||
local TS_IPS TS_IPV4
|
||||
TS_IPS=$(tailscale ip 2>/dev/null || echo "Unknown")
|
||||
TS_IPV4=$(echo "$TS_IPS" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -1 || echo "Unknown")
|
||||
print_success "Service tailscaled is active and connected. Node IPv4 in tailnet: $TS_IPV4"
|
||||
echo "$TS_IPS" > /tmp/tailscale_ips.txt
|
||||
else
|
||||
print_error "Service tailscaled is NOT active or not connected."
|
||||
local TS_SERVER="Not connected"
|
||||
local TS_FLAGS="None"
|
||||
FAILED_SERVICES+=("tailscaled")
|
||||
TS_COMMAND=$(grep "Tailscale connection failed: tailscale up" "$LOG_FILE" | tail -1 | sed 's/.*Tailscale connection failed: //')
|
||||
TS_COMMAND=${TS_COMMAND:-"tailscale up --operator=$USERNAME"}
|
||||
fi
|
||||
fi
|
||||
if [[ "$AUDIT_RAN" == true ]]; then
|
||||
print_success "Security audit performed."
|
||||
else
|
||||
print_info "Security audit not performed."
|
||||
fi
|
||||
echo -e "\n${GREEN}Server setup and hardening script has finished successfully.${NC}\n"
|
||||
echo -e "${YELLOW}Configuration Summary:${NC}"
|
||||
printf " %-16s%s\n" "Admin User:" "$USERNAME"
|
||||
@@ -1596,16 +1786,35 @@ generate_summary() {
|
||||
printf " %-16s%s\n" "- Remote Path:" "$REMOTE_BACKUP_PATH"
|
||||
printf " %-16s%s\n" "- Cron Schedule:" "$CRON_SCHEDULE"
|
||||
printf " %-16s%s\n" "- Notifications:" "$NOTIFICATION_STATUS"
|
||||
if [[ -f "$BACKUP_LOG" ]] && grep -q "Test backup successful" "$BACKUP_LOG" 2>/dev/null; then
|
||||
printf " %-16s%s\n" "- Test Status:" "Successful"
|
||||
elif [[ -f "$BACKUP_LOG" ]]; then
|
||||
printf " %-16s%s\n" "- Test Status:" "Failed (check $BACKUP_LOG)"
|
||||
else
|
||||
printf " %-16s%s\n" "- Test Status:" "Not run"
|
||||
fi
|
||||
else
|
||||
echo -e " Remote Backup: ${RED}Not configured${NC}"
|
||||
fi
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
local TS_SERVER=$(cat /tmp/tailscale_server 2>/dev/null || echo "https://controlplane.tailscale.com")
|
||||
local TS_IPS=$(cat /tmp/tailscale_ips.txt 2>/dev/null || echo "Not connected")
|
||||
local TS_FLAGS=$(cat /tmp/tailscale_flags 2>/dev/null || echo "None")
|
||||
echo -e " Tailscale: ${GREEN}Enabled${NC}"
|
||||
printf " %-16s%s\n" "- Server:" "$TS_SERVER"
|
||||
printf " %-16s%s\n" "- Tailscale IPs:" "$TS_IPS"
|
||||
printf " %-16s%s\n" "- Flags:" "$TS_FLAGS"
|
||||
else
|
||||
echo -e " Tailscale: ${RED}Not configured${NC}"
|
||||
fi
|
||||
if [[ "$AUDIT_RAN" == true ]]; then
|
||||
echo -e " Security Audit: ${GREEN}Performed${NC}"
|
||||
printf " %-16s%s\n" "- Audit Log:" "$AUDIT_LOG"
|
||||
printf " %-16s%s\n" "- Hardening Index:" "${HARDENING_INDEX:-Unknown}"
|
||||
printf " %-16s%s\n" "- Vulnerabilities:" "$DEBSECAN_VULNS"
|
||||
else
|
||||
echo -e " Security Audit: ${RED}Not run${NC}"
|
||||
fi
|
||||
echo
|
||||
printf "${PURPLE}%-16s%s${NC}\n" "Log File:" "$LOG_FILE"
|
||||
printf "${PURPLE}%-16s%s${NC}\n" "Backups:" "$BACKUP_DIR"
|
||||
@@ -1622,21 +1831,29 @@ generate_summary() {
|
||||
fi
|
||||
if command -v tailscale >/dev/null 2>&1; then
|
||||
printf " %-20s${CYAN}%s${NC}\n" "- Tailscale status:" "tailscale status"
|
||||
if [[ "$TS_SERVER" == "Not connected" && -n "$TS_COMMAND" ]]; then
|
||||
printf " %-20s${CYAN}%s${NC}\n" "- Tailscale connect:" "$TS_COMMAND"
|
||||
fi
|
||||
fi
|
||||
if [[ -f /root/run_backup.sh ]]; then
|
||||
echo -e " Remote Backup:"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Verify SSH key:" "cat /root/.ssh/id_ed25519.pub"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Copy key if needed:" "ssh-copy-id -p $BACKUP_PORT -s $BACKUP_DEST"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Test backup:" "sudo /root/run_backup.sh"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Check logs:" "sudo less /var/log/backup_rsync.log"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Check logs:" "sudo less $BACKUP_LOG"
|
||||
fi
|
||||
if [[ "$AUDIT_RAN" == true ]]; then
|
||||
echo -e " Security Audit:"
|
||||
printf " %-18s${CYAN}%s${NC}\n" "- Check results:" "sudo less $AUDIT_LOG"
|
||||
fi
|
||||
if [[ ${#FAILED_SERVICES[@]} -gt 0 ]]; then
|
||||
print_warning "ACTION REQUIRED: The following services failed: ${FAILED_SERVICES[*]}. Verify with 'systemctl status <service>'."
|
||||
fi
|
||||
print_warning "\nACTION REQUIRED: If remote backup is enabled, ensure the root SSH key is copied to the destination server."
|
||||
if [[ -n "$TS_COMMAND" ]]; then
|
||||
print_warning "ACTION REQUIRED: Tailscale connection failed. Run the command above to connect manually."
|
||||
print_warning "ACTION REQUIRED: Tailscale connection failed. Run the following command to connect manually:"
|
||||
echo -e "${CYAN} $TS_COMMAND${NC}"
|
||||
fi
|
||||
if [[ -f /root/run_backup.sh && "$KEY_COPY_CHOICE" == "2" ]]; then
|
||||
print_warning "ACTION REQUIRED: Ensure the root SSH key (/root/.ssh/id_ed25519.pub) is copied to $BACKUP_DEST."
|
||||
fi
|
||||
print_warning "ACTION REQUIRED: If remote backup is enabled, ensure the root SSH key is copied to the destination server."
|
||||
print_warning "A reboot is required to apply all changes cleanly."
|
||||
if [[ $VERBOSE == true ]]; then
|
||||
if confirm "Reboot now?" "y"; then
|
||||
@@ -1682,6 +1899,7 @@ main() {
|
||||
install_tailscale
|
||||
setup_backup
|
||||
configure_swap
|
||||
configure_security_audit
|
||||
final_cleanup
|
||||
generate_summary
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user