Ansible Security Automation: Bulk Server Hardening Configuration Guide
Ansible Playbooks for bulk Linux security-baseline hardening: SSH lockdown, firewall rules, system updates, and password policy — automated configuration.
Use Cases
Ansible security automation fits: tens to hundreds of Linux servers that need a uniform security baseline (SSH, firewall, password policy); new hosts that must be compliant before handover; security teams that want hardening as code — auditable, reversible, repeatable.
Prerequisites
- Control node (any Linux/macOS host) with Ansible 2.9+
- Targets running SSH; control node has passwordless login
- Targets are mainstream distros: CentOS/RHEL or Ubuntu/Debian
How It Works
Ansible is agentless. The control node SSHes to targets and runs modules. No client install on targets. Hardening is a Playbook: declarative desired state — SSH password login off, firewall allow-list, password expiry, etc. Idempotence means re-runs converge to the same state. `--check` previews diffs. Cuts blast radius on bulk change.
Procedure
Step 1: Install Ansible and write the inventory
pip install ansible
# Edit inventory /etc/ansible/hosts
[webservers]
web01 ansible_host=192.168.1.11
web02 ansible_host=192.168.1.12
# Test reachability
ansible webservers -m ping
Step 2: Write the hardening Playbook
# secure.yml
- hosts: webservers
become: yes
tasks:
- name: Disable SSH root login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
notify: restart sshd
- name: Firewall allow 22/80/443 only
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
immediate: yes
loop:
- 22/tcp
- 80/tcp
- 443/tcp
- name: Set password max age 90 days
lineinfile:
path: /etc/login.defs
regexp: '^PASS_MAX_DAYS'
line: 'PASS_MAX_DAYS 90'
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
Step 3: Check, then apply
# Dry-run: print planned changes only
ansible-playbook secure.yml --check
# Apply
ansible-playbook secure.yml
Verification
Re-run `ansible-playbook secure.yml`: all tasks `ok`, none `changed` — idempotent, desired state reached. Password SSH should be rejected. Root remote login fails. Firewall allows listed ports only; other ports time out.
FAQ
Q1: Hardening ran and I lost SSH. Now what?
Classic failure mode. Keep an existing session open first; that session stays up. Before changing SSH, test `ssh` from another window on the control node. Add `validate` on the sshd config task so a bad config rolls back.
Q2: Different hardening per environment?
Use `group_vars` and `host_vars`. Separate variable files per env (dev/test/prod). Prod forces `PermitRootLogin no` and a stricter password policy; dev can be looser. Playbook reads the vars.
Summary
Ansible turns repetitive manual hardening into reusable code. Write once, apply to the fleet. `--check` plus idempotence balances speed and safety. For multi-server estates, this is the step from manual ops to a standardized security baseline.