When implementing Ansible in production environments, the control node placement strategy significantly impacts your security posture. Let's examine the trade-offs:
# Option 1: Dedicated Control Node in Datacenter
# Pros: Centralized management, stable connection
# Cons: Single point of failure, requires hardening
# Option 2: Remote Workstation (e.g., laptop)
# Pros: Reduced attack surface, portable
# Cons: Availability concerns, endpoint security required
For web interfaces like Ansible Tower/AWX, implement these security measures:
- Deploy in DMZ with strict firewall rules
- Enable mandatory 2FA for all users
- Regularly rotate API tokens and credentials
The principle of least privilege should guide your SSH key implementation. Never use root SSH access directly. Instead:
# Recommended sudoers configuration (/etc/sudoers.d/ansible)
ansible ALL=(ALL) NOPASSWD: ALL
Defaults:ansible !requiretty
Key rotation best practices:
- Generate ED25519 keys:
ssh-keygen -t ed25519 -a 100 -f ansible_key
- Store private keys in encrypted vaults
- Implement short-lived certificates via HashiCorp Vault
Here's a secure inventory setup using ansible-vault:
# inventory/production.yml
all:
hosts:
web1:
ansible_host: 192.168.1.10
ansible_user: ansible_prod
ansible_ssh_private_key_file: "{{ vault_ssh_key }}"
ansible_become: true
Vault encryption command:
ansible-vault encrypt_string '-----BEGIN OPENSSH PRIVATE KEY-----...' \
--name 'vault_ssh_key'
Implement these network controls:
- SSH access restricted to control node IPs only
- Jump host architecture for sensitive environments
- Session logging with tools like auditd
When implementing Ansible in production environments, the control node's placement significantly impacts your security posture. Let's examine the trade-offs:
Dedicated Control Node in Datacenter:
Pros:
- Higher availability for CI/CD pipelines
- Centralized logging and monitoring
- Easier integration with web interfaces (Tower/Semaphore)
Cons:
- Single point of compromise
- Requires strict network segmentation
- Needs hardened OS configuration
Remote Workstation Model:
Pros:
- No persistent attack surface
- Temporary credentials can be used
- Follows zero-trust principles
Cons:
- Limited automation capabilities
- Human dependency for execution
- Challenging for team collaboration
A hybrid approach often works best: Use a hardened bastion host in a DMZ for web interfaces with strict access controls, while maintaining ephemeral control nodes for human operators.
For dedicated control nodes:
- Implement full disk encryption
- Configure mandatory access control (AppArmor/SELinux)
- Restrict network access with firewall rules:
# Example iptables rules for Ansible control node iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/24 -j ACCEPT iptables -A INPUT -j DROP
- Use centralized authentication (LDAP/SSSD)
For privilege delegation, never use root SSH directly. Instead:
# Example sudoers entry for Ansible user
ansible ALL=(ALL) NOPASSWD: /usr/bin/apt,/usr/bin/yum,/usr/bin/systemctl
Key recommendations:
- Create dedicated
ansible
user with restricted sudo rights - Use SSH certificates instead of raw keys (more revocable)
- Implement passphrase-protected keys with ssh-agent forwarding
- Rotate keys quarterly using Ansible Vault:
# Example playbook for key rotation
- name: Rotate SSH keys
hosts: all
vars_files:
- secrets.yml
tasks:
- name: Deploy new authorized_key
ansible.posix.authorized_key:
user: ansible
state: present
key: "{{ new_ssh_pub_key }}"
- name: Remove old authorized_key
ansible.posix.authorized_key:
user: ansible
state: absent
key: "{{ old_ssh_pub_key }}"
For Ansible Tower/Semaphore installations:
- Place behind reverse proxy with TLS 1.3
- Implement IP whitelisting
- Enable audit logging for all actions
- Configure session timeout (15 minutes recommended)
- Use hardware security modules (HSM) for credential storage
# Example nginx config for Tower
server {
listen 443 ssl;
ssl_certificate /etc/ssl/tower.crt;
ssl_certificate_key /etc/ssl/tower.key;
ssl_protocols TLSv1.3;
add_header Strict-Transport-Security "max-age=63072000";
location / {
proxy_pass http://localhost:8052;
allow 192.168.1.0/24;
deny all;
}
}