Practical Ansible Security Role Examples
Disclaimer: The examples, configurations, and code snippets provided in this article are for educational purposes only. While we strive for accuracy, there is no guarantee these will work in your specific environment. Always test configurations in a safe environment first and adapt them to your specific needs and security requirements.
When managing server infrastructure, implementing consistent security measures is crucial. Let’s build a practical system hardening role with Ansible that you can use as a foundation for your security automation needs.
Role Structure
Our system hardening role will follow this organization:
system_hardening/
├── tasks/
│ ├── main.yml
│ ├── packages.yml
│ ├── firewall.yml
│ └── ssh.yml
├── templates/
│ └── sshd_config.j2
└── defaults/
└── main.yml
Package Management
# tasks/packages.yml
---
- name: Update package cache
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Upgrade all packages
apt:
upgrade: yes
when: ansible_os_family == "Debian"
- name: Install security packages
apt:
name:
- ufw
- fail2ban
- rkhunter
- aide
state: present
when: ansible_os_family == "Debian"
Firewall Configuration
# tasks/firewall.yml
---
- name: Ensure UFW is installed
apt:
name: ufw
state: present
when: ansible_os_family == "Debian"
- name: Set default UFW policies
ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
- name: Allow SSH access
ufw:
rule: allow
port: "{{ ssh_port }}"
proto: tcp
- name: Enable UFW
ufw:
state: enabled
SSH Hardening
# tasks/ssh.yml
---
- name: Configure SSH security options
template:
src: sshd_config.j2
dest: /etc/ssh/sshd_config
owner: root
group: root
mode: '0600'
validate: /usr/sbin/sshd -t -f %s
notify: restart sshd
- name: Ensure SSH service is enabled and running
service:
name: sshd
state: started
enabled: yes
SSH Configuration Template
# templates/sshd_config.j2
# Security-hardened SSH configuration
Port {{ ssh_port }}
Protocol 2
# Authentication
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
# Security
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitUserEnvironment no
# Logging
SyslogFacility AUTH
LogLevel VERBOSE
# Timeouts
ClientAliveInterval 300
ClientAliveCountMax 2
LoginGraceTime 60
Default Variables
# defaults/main.yml
---
# SSH Configuration
ssh_port: 22
ssh_allowed_users: []
ssh_max_auth_tries: 3
ssh_client_alive_interval: 300
# Firewall Configuration
ufw_allowed_ports:
- "{{ ssh_port }}"
- 80
- 443
# Security Packages
security_packages:
- ufw
- fail2ban
- rkhunter
- aide
Main Tasks
# tasks/main.yml
---
- name: Include package management tasks
include_tasks: packages.yml
tags: [security, packages]
- name: Include firewall configuration
include_tasks: firewall.yml
tags: [security, firewall]
- name: Include SSH hardening
include_tasks: ssh.yml
tags: [security, ssh]
Using the Role
Create a playbook to apply the system hardening role:
# security.yml
---
- hosts: all
become: yes
roles:
- system_hardening
vars:
ssh_port: 2222 # Custom SSH port
ufw_allowed_ports:
- 2222 # Match custom SSH port
- 80
- 443
Handlers
# handlers/main.yml
---
- name: restart sshd
service:
name: sshd
state: restarted
- name: reload ufw
ufw:
state: reloaded
Testing
Create a test playbook using Molecule:
# molecule/default/converge.yml
---
- name: Converge
hosts: all
become: yes
roles:
- system_hardening
# molecule/default/verify.yml
---
- name: Verify
hosts: all
become: yes
tasks:
- name: Check UFW status
command: ufw status
register: ufw_status
failed_when: "'Status: active' not in ufw_status.stdout"
- name: Verify SSH configuration
command: sshd -T
register: ssh_config
failed_when: |
'permitrootlogin no' not in ssh_config.stdout.lower() or
'passwordauthentication no' not in ssh_config.stdout.lower()
Security Considerations
-
SSH Access
- Always verify SSH access works before ending your session
- Consider keeping a backup SSH port open during initial configuration
- Test key-based authentication before disabling password authentication
-
Firewall Rules
- Ensure all required ports are allowed before enabling UFW
- Consider rate limiting for services like SSH
- Document all opened ports and their purposes
-
Package Updates
- Test package updates in staging before production
- Consider using unattended-upgrades for automatic security updates
- Keep a list of critical packages that require manual updates
Conclusion
This system hardening role provides a solid foundation for securing your Linux servers. Key benefits include:
- Consistent security configurations across all systems
- Automated firewall setup and SSH hardening
- Easy customization through variables
- Comprehensive testing with Molecule
Remember to:
- Test thoroughly in a staging environment
- Customize settings based on your security requirements
- Keep the role updated with evolving security best practices
- Document any modifications or special configurations