Best Practices for Ansible Security Roles
Security in infrastructure automation requires careful planning and consistent practices. When creating Ansible roles for security tasks, following established best practices ensures your roles remain organized, maintainable, and secure. Let’s explore key guidelines for building effective security roles.
Role Organization
Keep Roles Focused
Each role should have a single, clear responsibility:
- Separate different security concerns into distinct roles (e.g., firewall, SSH, audit)
- Avoid mixing unrelated tasks within a single role
- Break down complex security configurations into smaller, manageable roles
Example directory structure for a focused role:
ssh_hardening/
├── tasks/
│ ├── main.yml # Task orchestration
│ ├── install.yml # Package installation
│ ├── configure.yml # SSH configuration
│ └── audit.yml # Security checks
├── templates/
│ └── sshd_config.j2 # SSH configuration template
└── defaults/
└── main.yml # Default variables
Use Role Dependencies
Declare role dependencies in meta/main.yml
to ensure proper execution order:
# meta/main.yml
dependencies:
- role: firewall
- role: ssh_hardening
- role: audit
Benefits:
- Ensures prerequisites are met before role execution
- Makes dependencies explicit and visible
- Maintains consistent security baseline across systems
Variable Management
Default Variables
Place default security settings in defaults/main.yml
:
# defaults/main.yml
---
security_ssh_port: 22
security_allowed_users: []
security_min_password_length: 12
security_password_complexity: true
Best practices:
- Use descriptive variable names with appropriate prefixes
- Document each variable’s purpose and impact
- Set secure defaults that follow principle of least privilege
Role-Specific Variables
Store internal role variables in vars/main.yml
:
# vars/main.yml
---
security_packages:
- ufw
- fail2ban
- aide
- rkhunter
security_services:
- name: sshd
state: running
enabled: true
Testing and Validation
Molecule Testing
Use Molecule for comprehensive role testing:
# molecule/default/converge.yml
---
- name: Converge
hosts: all
roles:
- role: security_baseline
# molecule/default/verify.yml
---
- name: Verify
hosts: all
tasks:
- name: Verify firewall status
command: ufw status
register: ufw_status
failed_when: "'Status: active' not in ufw_status.stdout"
- name: Check 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 Checks
Include comprehensive security validations:
- Verify service configurations
- Check file permissions
- Validate security policies
- Test network restrictions
Security Best Practices
Secret Management
Use Ansible Vault for sensitive data:
# group_vars/all/vault.yml
---
vault_api_tokens:
monitoring: !vault |
$ANSIBLE_VAULT;1.1;AES256
...
vault_ssl_certificates:
private_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
...
Logging and Monitoring
Implement proper logging for security events:
# tasks/logging.yml
---
- name: Configure audit logging
template:
src: auditd.conf.j2
dest: /etc/audit/auditd.conf
mode: '0600'
notify: restart auditd
- name: Enable security-related audit rules
template:
src: audit.rules.j2
dest: /etc/audit/rules.d/security.rules
mode: '0600'
notify: reload audit rules
File Permissions
Always set appropriate permissions:
# tasks/secure_files.yml
---
- name: Set secure permissions on configuration files
file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
owner: "{{ item.owner | default('root') }}"
group: "{{ item.group | default('root') }}"
loop:
- path: /etc/ssh/sshd_config
mode: '0600'
- path: /etc/sudoers
mode: '0440'
Documentation
Role Documentation
Maintain comprehensive documentation in README.md
:
# Security Baseline Role
This role implements security baseline configurations following CIS benchmarks.
## Requirements
- Ansible 2.9+
- Target systems: Ubuntu 20.04+
## Role Variables
| Variable | Default | Description |
|----------|---------|-------------|
| security_ssh_port | 22 | SSH port number |
| security_allowed_users | [] | List of allowed SSH users |
## Dependencies
- firewall
- ssh_hardening
## Example Playbook
```yaml
- hosts: servers
roles:
- role: security_baseline
vars:
security_ssh_port: 2222
Task Documentation
Include clear comments in tasks:
# tasks/main.yml
---
- name: Install security packages
apt:
name: "{{ security_packages }}"
state: present
update_cache: yes
tags: [security, packages]
# Ensures essential security tools are available
- name: Configure firewall rules
ufw:
rule: allow
port: "{{ security_ssh_port }}"
proto: tcp
tags: [security, firewall]
# Allows SSH access on configured port
Conclusion
Creating effective security roles requires attention to detail and adherence to best practices. By following these guidelines, you can build roles that are:
- Secure by default
- Easy to maintain
- Well-documented
- Thoroughly tested
- Reusable across projects
Remember to regularly review and update your roles as security requirements evolve and new best practices emerge.