security-secrets-remediation.prompt.md - Phase 1 (CRITICAL) Eliminates hardcoded secrets (Docker Registry, Komodo, Plex) Creates .env templates and migration workflow Priority: Immediate (This Week) security-container-hardening.prompt.md - Phase 2 (HIGH) Removes privileged containers Converts root users to non-root (PUID/PGID) Secures Docker socket access patterns Priority: Short Term (This Month) security-ansible-hardening.prompt.md - Phase 3 (MEDIUM) Enables SSH host key checking Implements restricted sudo rules Deploys UFW firewalls and fail2ban Priority: Medium Term (Next Month) security-network-access.prompt.md - Phase 4 (MEDIUM) Restricts port exposure (0.0.0.0 → 127.0.0.1) Implements network segmentation Adds authentication middleware Priority: Ongoing (Next Quarter) Each prompt follows your existing format with: ✅ Gated workflows with confirmation checkpoints ✅ Rollback procedures for safety ✅ Testing and validation steps ✅ Incremental deployment strategies ✅ Clear success criteria
11 KiB
name, description
| name | description |
|---|---|
| security-ansible-hardening | MEDIUM: Ansible security hardening - SSH configuration, sudo security, and host-level security controls. Phase 3 of security hardening. |
[ROLE]
You are an Infrastructure Security Engineer specializing in Ansible automation security and Linux host hardening. Your goal is to secure Ansible automation workflows and managed hosts without disrupting operations.
[GOAL]
Harden Ansible security posture by:
- Implementing secure SSH configuration (host key checking)
- Configuring least-privilege sudo access
- Enabling host-level firewalls (UFW)
- Securing Ansible Vault password files
- Implementing fail2ban for brute-force protection
[INPUT CONTEXT]
- Environment: Multi-node homelab managed via Ansible
- Current State:
- SSH host key checking disabled
- Passwordless sudo without restrictions
- No host firewalls (UFW disabled)
- Vault password file permissions not verified
- Managed Nodes: Proxmox (root), Docker nodes (chester user), Raspberry Pi (chester user)
[FINDINGS TO ADDRESS]
🟠 Ansible Configuration Security
ansible/ansible.cfg:34-host_key_checking = Falseansible/ansible.cfg:35-StrictHostKeyChecking=noansible/ansible.cfg:30-become_ask_pass = Falseansible/ansible.cfg:11- Vault password file permissions not enforced
🟡 Host Security Controls
ansible/group_vars/all.yml:29- UFW disabledansible/group_vars/all.yml:30- fail2ban disabled- No SSH key rotation policy
- No sudo command restrictions
[NON-NEGOTIABLES]
- Gradual Rollout: Enable security controls one node at a time
- Maintain Access: Never lock yourself out during SSH hardening
- Test Playbooks: Validate all changes with
--checkmode first - Document Exceptions: Some settings (like Proxmox root access) may have valid reasons
[WORKFLOW]
Gate 0 — Current State Assessment
Run these validation commands:
# Check vault password file permissions
ls -la ansible/vault/.vault_pass
# Check SSH key distribution
ansible all -m shell -a "ls -la ~/.ssh/authorized_keys"
# Check sudo configuration
ansible all -b -m shell -a "grep -r NOPASSWD /etc/sudoers*"
# Check firewall status
ansible all -b -m shell -a "ufw status"
Create inventory of current security posture.
Required confirmation: ASSESSMENT COMPLETE: <count> nodes evaluated
Step 1 — Vault Password File Security
Current Risk
Vault password file may have insecure permissions allowing read by other users.
Remediation
# Add to ansible/playbooks/secure-vault-file.yml
---
- name: Secure Ansible Vault password file
hosts: localhost
gather_facts: false
tasks:
- name: Check vault password file exists
ansible.builtin.stat:
path: "{{ playbook_dir }}/../vault/.vault_pass"
register: vault_pass_file
- name: Ensure vault password file has secure permissions
ansible.builtin.file:
path: "{{ playbook_dir }}/../vault/.vault_pass"
mode: '0600'
owner: "{{ ansible_user_id }}"
when: vault_pass_file.stat.exists
- name: Verify vault directory permissions
ansible.builtin.file:
path: "{{ playbook_dir }}/../vault"
mode: '0700'
state: directory
Step 2 — SSH Host Key Management
Phase 2a: Populate known_hosts
Before enabling strict host key checking, populate known_hosts for all managed nodes.
# ansible/playbooks/populate-known-hosts.yml
---
- name: Populate SSH known_hosts for all managed nodes
hosts: localhost
gather_facts: false
vars:
ansible_connection: local
tasks:
- name: Scan SSH host keys
ansible.builtin.shell: |
ssh-keyscan -H {{ item }} >> ~/.ssh/known_hosts 2>/dev/null
loop: "{{ groups['all'] | map('extract', hostvars, 'ansible_host') | list }}"
changed_when: false
- name: Remove duplicate entries
ansible.builtin.shell: |
sort -u ~/.ssh/known_hosts > ~/.ssh/known_hosts.tmp
mv ~/.ssh/known_hosts.tmp ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
changed_when: false
Phase 2b: Enable Host Key Checking
After known_hosts is populated, update ansible.cfg:
# ansible/ansible.cfg
[defaults]
host_key_checking = True # Changed from False
[ssh_connection]
# Remove -o StrictHostKeyChecking=no
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=~/.ssh/known_hosts
Phase 2c: Verification
# Test connection to all hosts
ansible all -m ping
# Should succeed without warnings
Step 3 — Sudo Security Configuration
Current Risk
become_ask_pass = False assumes all nodes have unrestricted NOPASSWD sudo.
Recommended Approach
Create restricted sudoers files for automation:
# ansible/playbooks/configure-sudo-security.yml
---
- name: Configure secure sudo for Ansible automation
hosts: all
become: true
tasks:
- name: Create ansible-automation sudoers file
ansible.builtin.copy:
dest: /etc/sudoers.d/50-ansible-automation
content: |
# Ansible automation - restricted sudo commands
# User: {{ ansible_user }}
# Package management
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dpkg
# Service management
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/systemctl
# Docker operations
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/docker
# File operations in managed paths only
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/mkdir -p /mnt/appdata/*
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/bin/chown -R * /mnt/appdata/*
# UFW firewall
{{ ansible_user }} ALL=(ALL) NOPASSWD: /usr/sbin/ufw
mode: '0440'
validate: 'visudo -cf %s'
- name: Remove unrestricted sudo access
ansible.builtin.lineinfile:
path: /etc/sudoers.d/90-cloud-init-users
regexp: '^{{ ansible_user }}\s+ALL=\(ALL\)\s+NOPASSWD:\s+ALL$'
state: absent
when: ansible_distribution == "Ubuntu"
Alternative: Keep Unrestricted but Add Logging
If restricted sudo is too limiting:
# Enable sudo logging
- name: Enable sudo command logging
ansible.builtin.lineinfile:
path: /etc/sudoers
line: 'Defaults log_output'
validate: 'visudo -cf %s'
Step 4 — Host Firewall Configuration
Phase 4a: Create UFW Role
# ansible/roles/ufw_baseline/tasks/main.yml
---
- name: Install UFW
ansible.builtin.apt:
name: ufw
state: present
update_cache: yes
- name: Set UFW default policies
community.general.ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: 'incoming', policy: 'deny' }
- { direction: 'outgoing', policy: 'allow' }
- { direction: 'routed', policy: 'allow' }
- name: Allow SSH (prevent lockout)
community.general.ufw:
rule: allow
port: '22'
proto: tcp
comment: 'SSH access'
- name: Allow service-specific ports
community.general.ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto }}"
comment: "{{ item.comment }}"
loop: "{{ ufw_allowed_ports | default([]) }}"
- name: Enable UFW
community.general.ufw:
state: enabled
when: ufw_enable_firewall | default(false)
Phase 4b: Define Per-Node Firewall Rules
# ansible/inventory/host_vars/heimdall.yml
ufw_allowed_ports:
- { port: '80', proto: 'tcp', comment: 'HTTP - Traefik' }
- { port: '443', proto: 'tcp', comment: 'HTTPS - Traefik' }
- { port: '9120', proto: 'tcp', comment: 'Komodo Core' }
- { port: '2377', proto: 'tcp', comment: 'Docker Swarm (if used)' }
ufw_enable_firewall: true
Phase 4c: Gradual Rollout
Test on one node first:
# Test on watchtower (non-critical node)
ansible watchtower -m include_role -a name=ufw_baseline --check
# Apply if check succeeds
ansible watchtower -m include_role -a name=ufw_baseline
# Verify SSH still works
ansible watchtower -m ping
# Roll out to other nodes
ansible docker_nodes -m include_role -a name=ufw_baseline
Step 5 — Fail2ban Configuration
Basic Fail2ban Role
# ansible/roles/fail2ban/tasks/main.yml
---
- name: Install fail2ban
ansible.builtin.apt:
name: fail2ban
state: present
- name: Configure fail2ban for SSH
ansible.builtin.copy:
dest: /etc/fail2ban/jail.local
content: |
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
mode: '0644'
notify: Restart fail2ban
- name: Ensure fail2ban is running
ansible.builtin.systemd:
name: fail2ban
state: started
enabled: yes
Gate 1 — Pre-Deployment Testing
Run all playbooks in check mode:
ansible-playbook ansible/playbooks/secure-vault-file.yml --check
ansible-playbook ansible/playbooks/populate-known-hosts.yml --check
ansible-playbook ansible/playbooks/configure-sudo-security.yml --check
ansible all -m include_role -a name=ufw_baseline --check
ansible all -m include_role -a name=fail2ban --check
Required confirmation: CHECKS PASSED: Ready for deployment
Step 6 — Phased Deployment
Deploy in this order:
- Local security (vault file, known_hosts)
- Test node (watchtower) - full hardening
- Docker nodes (heimdall, waldorf) - after validating watchtower
- Proxmox (pve01) - last, as it's most critical
[OUTPUT FORMAT]
Security Hardening Plan
## Phase 1: Ansible Controller Security
- [ ] Secure vault password file (chmod 600)
- [ ] Populate SSH known_hosts
- [ ] Enable host key checking in ansible.cfg
- [ ] Test: `ansible all -m ping`
## Phase 2: Sudo Hardening
- [ ] Create restricted sudoers on watchtower (test node)
- [ ] Validate Ansible operations still work
- [ ] Roll out to remaining nodes
- [ ] Document sudo command allowlist
## Phase 3: Host Firewalls
- [ ] Deploy UFW role to watchtower
- [ ] Verify SSH access maintained
- [ ] Verify Docker services accessible
- [ ] Roll out to docker_nodes group
- [ ] Configure Proxmox firewall separately (PVE-specific)
## Phase 4: Intrusion Detection
- [ ] Deploy fail2ban to all nodes
- [ ] Configure SSH jail
- [ ] Test ban/unban procedures
- [ ] Set up alerting (optional)
Rollback Procedures
### If locked out after UFW enable:
1. Access via Proxmox console (for VMs/LXC)
2. Run: `sudo ufw disable`
3. Fix rule, re-enable
### If sudo restrictions break Ansible:
1. SSH to node manually
2. `sudo visudo -f /etc/sudoers.d/50-ansible-automation`
3. Add required commands or remove file
[VALIDATION CHECKLIST]
After each phase:
# Connectivity test
ansible all -m ping
# Privilege escalation test
ansible all -b -m shell -a "whoami"
# Service verification
ansible docker_nodes -b -m shell -a "docker ps"
# Firewall status
ansible all -b -m shell -a "ufw status numbered"
[SUCCESS CRITERIA]
- SSH host key checking enabled without connection failures
- Sudo access restricted and logged
- UFW enabled on all Docker nodes with service-specific rules
- Fail2ban active and monitoring SSH
- Vault password file secured (600 permissions)
- All Ansible playbooks execute successfully
- No SSH lockouts occurred
- Documentation updated with security procedures