Security and Alerting Setup
This guide will walk you through setting up advanced security features for your home server, including automatic Tor exit node blocking, comprehensive alerting via email and Slack, and Docker profiles for service isolation. These features will help:
- Protect your services from malicious actors
- Get notified about important events and potential security threats
- Organize your Docker services for better management
Automatic Tor Exit Node Blocking
Tor exit nodes can be used by malicious actors to attack your services. Let's set up automatic blocking of Tor exit nodes using a script and cron job.
Directory Setup
First, create a directory for the security scripts:
mkdir -p ~/docker/security/tor-blocking
cd ~/docker/security/tor-blocking
Tor Exit Node Blocking Script
Create a script to fetch and block Tor exit nodes:
nano ~/docker/security/tor-blocking/block-tor-exits.sh
Add the following content:
#!/bin/bash
# Script to block Tor exit nodes
# This script fetches the current list of Tor exit nodes and blocks them using iptables
# Log file
LOG_FILE="/var/log/tor-blocking.log"
# Create log file if it doesn't exist
if [ ! -f "$LOG_FILE" ]; then
sudo touch "$LOG_FILE"
sudo chmod 644 "$LOG_FILE"
fi
# Log function
log() {
echo "$(date): $1" | sudo tee -a "$LOG_FILE"
}
log "Starting Tor exit node blocking script"
# Create a new ipset if it doesn't exist
if ! sudo ipset list tor-exits &>/dev/null; then
log "Creating new ipset for Tor exit nodes"
sudo ipset create tor-exits hash:ip hashsize 4096
fi
# Flush the existing ipset
log "Flushing existing Tor exit node list"
sudo ipset flush tor-exits
# Fetch the current list of Tor exit nodes
log "Fetching current Tor exit node list"
TOR_EXITS=$(curl -s https://check.torproject.org/exit-addresses | grep ExitAddress | cut -d ' ' -f 2)
# Count of exit nodes
EXIT_COUNT=$(echo "$TOR_EXITS" | wc -l)
log "Found $EXIT_COUNT Tor exit nodes"
# Add each exit node to the ipset
for IP in $TOR_EXITS; do
sudo ipset add tor-exits $IP
done
# Check if the iptables rule exists, if not add it
if ! sudo iptables -C INPUT -m set --match-set tor-exits src -j DROP 2>/dev/null; then
log "Adding iptables rule to block Tor exit nodes"
sudo iptables -A INPUT -m set --match-set tor-exits src -j DROP
sudo iptables -A FORWARD -m set --match-set tor-exits src -j DROP
fi
# Make iptables rules persistent
if command -v netfilter-persistent &>/dev/null; then
log "Saving iptables rules"
sudo netfilter-persistent save
fi
log "Tor exit node blocking updated successfully"
# Send alert to Prometheus Alertmanager
if [ -n "$EXIT_COUNT" ] && [ "$EXIT_COUNT" -gt 0 ]; then
log "Sending alert to Alertmanager"
curl -XPOST http://localhost:9093/api/v1/alerts -H "Content-Type: application/json" -d "[{
\"labels\": {
\"alertname\": \"TorExitNodesBlocked\",
\"severity\": \"info\",
\"instance\": \"$(hostname)\"
},
\"annotations\": {
\"summary\": \"Tor exit nodes blocked\",
\"description\": \"$EXIT_COUNT Tor exit nodes have been blocked\"
}
}]"
fi
exit 0
Make the script executable:
chmod +x ~/docker/security/tor-blocking/block-tor-exits.sh
Install Required Packages
Install the necessary packages for IP blocking:
sudo apt update
sudo apt install -y ipset iptables-persistent curl
Set Up Cron Job
Set up a cron job to run the script every hour:
sudo crontab -e
Add the following line:
0 * * * * /home/ubuntu/docker/security/tor-blocking/block-tor-exits.sh
Run the Script Manually
Run the script manually to verify it works:
sudo ~/docker/security/tor-blocking/block-tor-exits.sh
Check the log file:
cat /var/log/tor-blocking.log
Fail2Ban Integration
Fail2Ban can help protect your server from brute force attacks. Let's set it up to work with our services.
Install Fail2Ban
sudo apt install -y fail2ban
Configure Fail2Ban for SSH
Create a custom SSH jail configuration:
sudo nano /etc/fail2ban/jail.d/ssh.conf
Add the following content:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400
Configure Fail2Ban for Nginx
Create a custom Nginx jail configuration:
sudo nano /etc/fail2ban/jail.d/nginx.conf
Add the following content:
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 86400
[nginx-badbots]
enabled = true
filter = nginx-badbots
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 86400
Restart Fail2Ban
sudo systemctl restart fail2ban
Check Fail2Ban Status
sudo fail2ban-client status
Email Alerting Setup
Let's configure email alerting for our monitoring system.
Configure Postfix for Email Relay
Install Postfix for email sending:
sudo apt install -y postfix mailutils
During installation, select "Internet Site" and enter your domain name.
Edit the Postfix configuration:
sudo nano /etc/postfix/main.cf
Update the following settings:
myhostname = yourdomain.com
mydomain = yourdomain.com
myorigin = $mydomain
relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
Create the SASL password file:
sudo nano /etc/postfix/sasl_passwd
Add your Gmail credentials:
[smtp.gmail.com]:587 your-email@gmail.com:your-app-password
Generate the hash database and set permissions:
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd.db
Restart Postfix:
sudo systemctl restart postfix
Test email sending:
echo "Test email from your home server" | mail -s "Test Alert" your-email@example.com
Update Alert Manager Configuration
Update the Alert Manager configuration to use the local mail relay:
nano ~/docker/monitoring/alertmanager/config/alertmanager.yml
Update the email configuration:
global:
resolve_timeout: 5m
smtp_smarthost: 'localhost:25'
smtp_from: 'alerts@yourdomain.com'
smtp_require_tls: false
# Rest of the configuration remains the same
Restart Alert Manager:
cd ~/docker/monitoring
docker-compose restart alertmanager
Slack Alerting Setup
Let's configure Slack alerting for our monitoring system.
Create a Slack Webhook
- Go to your Slack workspace
- Create a new channel for alerts (e.g., #server-alerts)
- Go to https://api.slack.com/apps and create a new app
- Enable "Incoming Webhooks" for your app
- Create a new webhook URL for your alerts channel
- Copy the webhook URL
Update Alert Manager Configuration
Update the Alert Manager configuration to use the Slack webhook:
nano ~/docker/monitoring/alertmanager/config/alertmanager.yml
Ensure the Slack configuration is properly set:
receivers:
- name: 'slack-notifications'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#server-alerts'
send_resolved: true
title: "{{ .GroupLabels.alertname }}"
text: "{{ range .Alerts }}{{ .Annotations.description }}\n{{ end }}"
Replace 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
with your actual webhook URL.
Restart Alert Manager:
cd ~/docker/monitoring
docker-compose restart alertmanager
Docker Profiles Configuration
Docker Compose profiles allow you to selectively start services. Let's organize our services into logical profiles.
Update Docker Compose Files
We've already added profile configurations to our Docker Compose files. Let's create a script to manage them all:
nano ~/docker/manage-services.sh
Add the following content:
#!/bin/bash
# Script to manage Docker Compose services using profiles
# Function to display usage
usage() {
echo "Usage: $0 [command] [profile]"
echo "Commands:"
echo " start - Start services with the specified profile"
echo " stop - Stop services with the specified profile"
echo " restart - Restart services with the specified profile"
echo " status - Show status of all services"
echo "Profiles:"
echo " all - All services"
echo " proxy - Nginx Proxy Manager and Authelia"
echo " auth - Authelia only"
echo " monitoring - Prometheus, Grafana, and Alert Manager"
echo " logging - Loki and Promtail"
echo " security - Security-related services"
exit 1
}
# Check if command is provided
if [ $# -lt 1 ]; then
usage
fi
COMMAND=$1
PROFILE=$2
# Function to start services
start_services() {
case $1 in
all)
echo "Starting all services..."
cd ~/docker/nginx-proxy-manager && docker-compose --profile proxy up -d
cd ~/docker/authelia && docker-compose --profile auth up -d
cd ~/docker/monitoring && docker-compose --profile monitoring up -d
cd ~/docker/logging && docker-compose --profile logging up -d
;;
proxy)
echo "Starting proxy services..."
cd ~/docker/nginx-proxy-manager && docker-compose --profile proxy up -d
;;
auth)
echo "Starting authentication services..."
cd ~/docker/authelia && docker-compose --profile auth up -d
;;
monitoring)
echo "Starting monitoring services..."
cd ~/docker/monitoring && docker-compose --profile monitoring up -d
;;
logging)
echo "Starting logging services..."
cd ~/docker/logging && docker-compose --profile logging up -d
;;
security)
echo "Starting security services..."
# Add security services here when available
;;
*)
echo "Unknown profile: $1"
usage
;;
esac
}
# Function to stop services
stop_services() {
case $1 in
all)
echo "Stopping all services..."
cd ~/docker/logging && docker-compose --profile logging down
cd ~/docker/monitoring && docker-compose --profile monitoring down
cd ~/docker/authelia && docker-compose --profile auth down
cd ~/docker/nginx-proxy-manager && docker-compose --profile proxy down
;;
proxy)
echo "Stopping proxy services..."
cd ~/docker/nginx-proxy-manager && docker-compose --profile proxy down
;;
auth)
echo "Stopping authentication services..."
cd ~/docker/authelia && docker-compose --profile auth down
;;
monitoring)
echo "Stopping monitoring services..."
cd ~/docker/monitoring && docker-compose --profile monitoring down
;;
logging)
echo "Stopping logging services..."
cd ~/docker/logging && docker-compose --profile logging down
;;
security)
echo "Stopping security services..."
# Add security services here when available
;;
*)
echo "Unknown profile: $1"
usage
;;
esac
}
# Function to restart services
restart_services() {
stop_services $1
start_services $1
}
# Function to show status
show_status() {
echo "Docker containers status:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
}
# Execute command
case $COMMAND in
start)
if [ -z "$PROFILE" ]; then
echo "Error: Profile not specified"
usage
fi
start_services $PROFILE
;;
stop)
if [ -z "$PROFILE" ]; then
echo "Error: Profile not specified"
usage
fi
stop_services $PROFILE
;;
restart)
if [ -z "$PROFILE" ]; then
echo "Error: Profile not specified"
usage
fi
restart_services $PROFILE
;;
status)
show_status
;;
*)
echo "Unknown command: $COMMAND"
usage
;;
esac
exit 0
Make the script executable:
chmod +x ~/docker/manage-services.sh
Using the Service Management Script
Start all services:
~/docker/manage-services.sh start all
Start only monitoring services:
~/docker/manage-services.sh start monitoring
Check the status of all services:
~/docker/manage-services.sh status
Security Monitoring Dashboard
Let's create a security monitoring dashboard in Grafana to visualize security events.
Create Security Dashboard Configuration
mkdir -p ~/docker/monitoring/grafana/dashboards
nano ~/docker/monitoring/grafana/dashboards/security-dashboard.json
Add the JSON configuration for the security dashboard (refer to the original documentation for the full JSON content).
Update Grafana Configuration
To automatically load the dashboard, update the Grafana Docker Compose configuration:
nano ~/docker/monitoring/docker-compose.yml
Add a volume for the dashboards:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/var/lib/grafana/dashboards # Add this line
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=secure_password
- GF_USERS_ALLOW_SIGN_UP=false
- GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH=/var/lib/grafana/dashboards/security-dashboard.json # Add this line
ports:
- "3000:3000"
networks:
- proxy
profiles:
- monitoring
Restart Grafana:
cd ~/docker/monitoring
docker-compose restart grafana
System Backup Script
Let's create a script to back up your important configuration files:
nano ~/docker/backup-configs.sh
Add the following content:
#!/bin/bash
# Script to back up important configuration files
# Backup directory
BACKUP_DIR="/home/ubuntu/backups"
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
BACKUP_FILE="$BACKUP_DIR/homeserver-backup-$TIMESTAMP.tar.gz"
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Log file
LOG_FILE="$BACKUP_DIR/backup-$TIMESTAMP.log"
# Log function
log() {
echo "$(date): $1" | tee -a "$LOG_FILE"
}
log "Starting backup process"
# Directories to back up
DIRS_TO_BACKUP=(
"/home/ubuntu/docker/nginx-proxy-manager/data"
"/home/ubuntu/docker/authelia/config"
"/home/ubuntu/docker/monitoring/prometheus/config"
"/home/ubuntu/docker/monitoring/alertmanager/config"
"/home/ubuntu/docker/logging/loki/config"
"/home/ubuntu/docker/logging/promtail/config"
"/home/ubuntu/docker/security"
"/etc/fail2ban"
)
# Create temporary directory
TEMP_DIR=$(mktemp -d)
log "Created temporary directory: $TEMP_DIR"
# Copy files to temporary directory
for DIR in "${DIRS_TO_BACKUP[@]}"; do
if [ -d "$DIR" ]; then
TARGET_DIR="$TEMP_DIR$(dirname "$DIR")"
mkdir -p "$TARGET_DIR"
cp -r "$DIR" "$TARGET_DIR"
log "Copied $DIR to $TARGET_DIR"
else
log "Warning: Directory $DIR does not exist, skipping"
fi
done
# Create tar archive
tar -czf "$BACKUP_FILE" -C "$TEMP_DIR" .
log "Created backup archive: $BACKUP_FILE"
# Clean up temporary directory
rm -rf "$TEMP_DIR"
log "Cleaned up temporary directory"
# Keep only the 5 most recent backups
ls -t "$BACKUP_DIR"/homeserver-backup-*.tar.gz | tail -n +6 | xargs -r rm
log "Removed old backups, keeping the 5 most recent"
# Send email notification
if command -v mail &>/dev/null; then
echo "Home server backup completed. Backup file: $BACKUP_FILE" | mail -s "Home Server Backup Completed" your-email@example.com
log "Sent email notification"
fi
log "Backup process completed successfully"
exit 0
Make the script executable:
chmod +x ~/docker/backup-configs.sh
Set up a cron job to run the backup script weekly:
crontab -e
Add the following line:
0 2 * * 0 /home/ubuntu/docker/backup-configs.sh
Next Steps
You have now completed the setup of a comprehensive home server with:
- Secure access through Nginx Proxy Manager and Authelia
- Monitoring with Prometheus and Grafana
- Logging with Loki and Promtail
- Security features including Tor exit node blocking
- Alerting via email and Slack
- Docker profiles for service organization
To further enhance your home server, consider:
- Setting up additional services like Nextcloud for file storage
- Implementing a VPN server for secure remote access
- Adding a media server like Plex or Jellyfin
- Setting up home automation with Home Assistant
- Implementing regular security audits and updates
Remember to regularly update your system and Docker containers to ensure you have the latest security patches.