Comprehensive Ansible automation for securing Ubuntu servers with hardening, monitoring, and Cloudflare Tunnel integration. Based on a three-part article series covering best practices for server security.
- System Updates: Automatic security updates with unattended-upgrades
- User Management: Admin and application user creation with SSH key authentication
- SSH Hardening: Key-only authentication, root login disabled, connection limits
- Firewall (UFW): Deny all incoming, rate-limited SSH, custom rules support
- Fail2Ban: Intrusion prevention with configurable jails
- Kernel Hardening: Security-focused kernel parameter tuning
- Zero Trust network access
- No open ports required (after initial setup)
- SSH through Cloudflare Tunnel
- Application exposure with authentication
- Automatic configuration and service setup
- Prometheus: Metrics collection and alerting
- Node Exporter: System metrics (CPU, memory, disk, network)
- Grafana: Beautiful dashboards with pre-configured templates
- Alertmanager: Email and webhook alert notifications
- Alert Rules: CPU, memory, disk, load average, and uptime alerts
- Secure Access: Expose via Cloudflare Tunnel with Zero Trust
- Automated configuration backups
- Configurable retention period (default 30 days)
- Remote storage support (S3, DigitalOcean Spaces)
- One-command restore
- Rollback capabilities for safe experimentation
- Idempotent: Run multiple times safely
- Modular: Use tags to run specific roles
- Check Mode: Preview changes before applying
- Comprehensive Logging: Audit trail of all changes
- Rollback Support: Undo changes if needed
- Ubuntu 20.04 LTS or 22.04 LTS server (freshly installed)
- SSH access with root or sudo privileges
- Ansible 2.10+ on your control machine
- (Optional) Cloudflare account for tunnel setup
-
Clone the repository
git clone https://github.com/annurdien/ubuntu-server-setup.git cd ubuntu-server-setup -
Install Ansible (if not already installed)
sudo apt update sudo apt install -y ansible
-
Configure your setup
# Copy and edit inventory cp inventory.ini.example inventory.ini vim inventory.ini # Add your server IPs # Copy and edit configuration cp group_vars/all.yml.example group_vars/all.yml vim group_vars/all.yml # Update with your settings
-
Add your SSH public key
# Generate if you don't have one ssh-keygen -t ed25519 -C "admin@server" # Copy public key to files directory cp ~/.ssh/id_ed25519.pub files/ssh_keys/admin_id_ed25519.pub
-
Run the quick setup script
./scripts/quick-setup.sh
ansible all -m pingansible-playbook playbook.yml# Copy monitoring configuration
cp group_vars/monitoring.yml.example group_vars/monitoring.yml
vim group_vars/monitoring.yml # Update settings
# Run monitoring playbook
ansible-playbook monitoring.yml# Admin user
admin_user:
name: "admin"
password: "{{ 'YourSecurePassword' | password_hash('sha512') }}"
ssh_key: "{{ lookup('file', 'files/ssh_keys/admin_id_ed25519.pub') }}"
# SSH settings
ssh_port: 22
ssh_permit_root_login: "no"
ssh_password_authentication: "no"
# Firewall
ufw_enabled: true
ufw_ssh_rate_limit: true
# Fail2Ban
fail2ban_enabled: true
fail2ban_bantime: 3600
fail2ban_maxretry: 5
# Cloudflare Tunnel (optional)
cloudflare_tunnel_token: "your-token"
domain_name: "example.com"See group_vars/all.yml.example for complete configuration options.
# Only update system packages
ansible-playbook playbook.yml --tags common
# Only configure SSH
ansible-playbook playbook.yml --tags ssh
# Only setup firewall
ansible-playbook playbook.yml --tags firewall
# Setup security (users, SSH, firewall, fail2ban)
ansible-playbook playbook.yml --tags security# Create backup
ansible-playbook backup-restore.yml
# Select option 1
# Restore from latest backup
ansible-playbook backup-restore.yml
# Select option 2
# List all backups
ansible-playbook backup-restore.yml
# Select option 4# Interactive rollback
ansible-playbook rollback.yml
# Follow prompts to select what to rollback# Preview changes without applying
ansible-playbook playbook.yml --check
# Preview with detailed output
ansible-playbook playbook.yml --check --diff-
Test SSH Access
ssh admin@your-server-ip
-
Check Firewall Status
sudo ufw status verbose
-
Verify Fail2Ban
sudo fail2ban-client status sshd
-
Test Cloudflare Tunnel (if configured)
ssh admin@ssh.yourdomain.com
If you deployed the monitoring stack:
- Grafana: https://grafana.yourdomain.com (if using tunnel) or http://server-ip:3000
- Default login: admin / admin (change immediately!)
- Prometheus: http://server-ip:9090
- Alertmanager: http://server-ip:9093
After verifying Cloudflare Tunnel works:
# Close SSH port (access only via tunnel)
sudo ufw delete allow 22/tcp
# Verify
sudo ufw status# Check SSH status
sudo systemctl status sshd
# View SSH logs
sudo journalctl -u sshd -n 50
# Test SSH configuration
sudo sshd -t# Check UFW status
sudo ufw status numbered
# View UFW logs
sudo tail -f /var/log/ufw.log
# Temporarily disable UFW (emergency only!)
sudo ufw disable# Check Fail2Ban status
sudo fail2ban-client status sshd
# View banned IPs
sudo fail2ban-client get sshd banip
# Check logs
sudo tail -f /var/log/fail2ban.log# Check service status
sudo systemctl status prometheus
sudo systemctl status grafana-server
sudo systemctl status alertmanager
# View logs
sudo journalctl -u prometheus -f
sudo journalctl -u grafana-server -fAdd to group_vars/all.yml:
app_users:
- name: "webapp"
comment: "Web Application User"
system: true
shell: "/usr/sbin/nologin"ufw_allowed_ports:
- { port: 80, proto: tcp }
- { port: 443, proto: tcp }
- { port: 8080, proto: tcp }fail2ban_custom_jails:
- name: "nginx-limit-req"
port: "http,https"
filter: "nginx-limit-req"
logpath: "/var/log/nginx/error.log"backup_remote_enabled: true
backup_remote_type: "s3"
backup_remote_bucket: "my-backups"
backup_remote_access_key: "your-key"
backup_remote_secret_key: "your-secret"-
Never commit sensitive data
- SSH private keys
- Cloudflare tokens
- API credentials
- Server IP addresses
- Actual configuration files
-
Use strong passwords
- Generate strong passwords for all users
- Use password managers
- Rotate credentials regularly
-
SSH key management
- Use Ed25519 keys (more secure)
- Protect private keys with passphrases
- Never share private keys
- Rotate keys periodically
-
Cloudflare Tunnel security
- Enable Zero Trust authentication
- Use WARP for SSH access
- Regularly review access logs
- Rotate tunnel tokens
-
Monitor your systems
- Review Fail2Ban logs regularly
- Check Prometheus alerts
- Monitor system metrics
- Keep systems updated
-
Test before production
- Always test in a development environment
- Use
--checkmode first - Keep backups before changes
- Document your changes
┌─────────────────────────────────────────────┐
│ Cloudflare Network │
│ ┌──────────────────────────────────────┐ │
│ │ Cloudflare Tunnel + Zero Trust │ │
│ └──────────────────────────────────────┘ │
└─────────────────┬───────────────────────────┘
│ Encrypted Tunnel
┌─────────────────▼───────────────────────────┐
│ Ubuntu Server │
│ ┌──────────────────────────────────────┐ │
│ │ cloudflared │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ SSH │ Apps │ Monitoring │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ UFW Firewall (deny all) │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Fail2Ban │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
.
├── README.md # This file
├── DEPLOYMENT_GUIDE.md # Step-by-step deployment guide
├── ansible.cfg # Ansible configuration
├── inventory.ini.example # Example inventory
├── playbook.yml # Main security playbook
├── rollback.yml # Rollback playbook
├── backup-restore.yml # Backup/restore playbook
├── monitoring.yml # Monitoring stack playbook
├── group_vars/
│ ├── all.yml.example # Main configuration
│ └── monitoring.yml.example # Monitoring configuration
├── roles/
│ ├── common/ # System updates & base config
│ ├── users/ # User management
│ ├── ssh/ # SSH hardening
│ ├── firewall/ # UFW configuration
│ ├── fail2ban/ # Fail2Ban setup
│ ├── cloudflared/ # Cloudflare Tunnel
│ ├── backup/ # Backup automation
│ ├── restore/ # Restore automation
│ ├── prometheus/ # Prometheus setup
│ ├── node_exporter/ # System metrics
│ ├── grafana/ # Dashboards
│ ├── alertmanager/ # Alert management
│ └── monitoring_tunnel/ # Monitoring via tunnel
├── files/
│ └── ssh_keys/ # SSH public keys
└── scripts/
├── quick-setup.sh # Quick deployment script
└── create-repo.sh # Repository structure setup
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Test your changes thoroughly
- Submit a pull request
MIT License - See LICENSE file for details
- Issues: https://github.com/annurdien/ubuntu-server-setup/issues
- Discussions: https://github.com/annurdien/ubuntu-server-setup/discussions
Based on the three-part Ubuntu server security article series covering:
- Initial server hardening and security setup
- Cloudflare Tunnel integration with Zero Trust
- Monitoring with Prometheus and Grafana
- Ansible Documentation
- Ubuntu Security Guide
- Cloudflare Tunnel Documentation
- Prometheus Documentation
- Grafana Documentation
⚡ Quick Commands Reference
# Deploy everything
ansible-playbook playbook.yml
# Setup monitoring
ansible-playbook monitoring.yml
# Create backup
ansible-playbook backup-restore.yml
# Rollback changes
ansible-playbook rollback.yml
# Check connection
ansible all -m ping
# Dry run
ansible-playbook playbook.yml --check