Practical Ansible Security Role Examples
April 17, 2024
ansible
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