Restructure into one folder per private repo/host, not a flat Docker/ dump
rocky-docker-lxc/, caddy-vm/, monitoring-vm/ (was missing entirely -
Grafana+Loki+Prometheus stack, refreshed and CA mount stripped same as
everything else), and automation/{ansible,automation-vm,iac}/ for the
three repos that live on the automation VM. Hawser is genuinely
deployed on all three of caddy-vm/monitoring-vm/automation-vm so it's
duplicated in each rather than picking one canonical home.
Old Ansible/ and "Infrastrucutre as Code/" content moved under
automation/ansible/old-playbooks and automation/iac/old-content as-is
for now - contents still need refreshing against the current repos and
a per-folder README, that's the next pass.
This commit is contained in:
146 files changed
+101
-84
No files matched your search
@@ -0,0 +1,11 @@
|
||||
---
|
||||
cis_banner: |
|
||||
*********************************************************************
|
||||
* WARNING: Unauthorized access to this system is prohibited. *
|
||||
* All activities are monitored and recorded. *
|
||||
*********************************************************************
|
||||
|
||||
cis_motd_banner: |
|
||||
*********************************************************************
|
||||
* Welcome to the system. Access is restricted to authorized users. *
|
||||
*********************************************************************
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
# Login banners for /etc/motd, /etc/issue, /etc/issue.net
|
||||
|
||||
- name: Ensure /etc/motd contains standard CIS banner
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/motd
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: "{{ cis_motd_banner }}"
|
||||
|
||||
- name: Ensure /etc/issue contains standard CIS banner
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/issue
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: "{{ cis_motd_banner }}"
|
||||
|
||||
- name: Ensure /etc/issue.net contains standard CIS banner
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/issue.net
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: "{{ cis_banner }}"
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
# Parent-level defaults
|
||||
cis_tag: cis_hardening
|
||||
audit_rules_file: /etc/audit/rules.d/cis.rules
|
||||
rsyslog_remote: "" # set to "logserver.example.com:514" to forward logs
|
||||
txt_grub_args: "" # additional kernel args if needed, e.g. "audit=1"
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
---
|
||||
# Default ownership and permissions for critical files and directories
|
||||
|
||||
file_permissions_list:
|
||||
- { path: "/root", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/passwd", mode: "0644", state: "file" }
|
||||
- { path: "/etc/shadow", mode: "0400", state: "file" }
|
||||
- { path: "/etc/group", mode: "0644", state: "file" }
|
||||
- { path: "/etc/gshadow", mode: "0400", state: "file" }
|
||||
- { path: "/etc/crontab", mode: "0600", state: "file" }
|
||||
- { path: "/etc/cron.hourly", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/cron.daily", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/cron.weekly", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/cron.monthly", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/cron.d", mode: "0700", state: "directory" }
|
||||
- { path: "/etc/rsyslog.conf", mode: "0640", state: "file" }
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Ensure system files and directories have correct ownership and permissions
|
||||
ansible.builtin.file:
|
||||
path: "{{ file_item.path }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "{{ file_item.mode }}"
|
||||
state: "{{ file_item.state }}"
|
||||
loop: "{{ file_permissions_list }}"
|
||||
loop_control:
|
||||
loop_var: file_item
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
tmp_mount_options: "defaults,nosuid,nodev,noexec,relatime"
|
||||
var_tmp_mount_options: "defaults,nosuid,nodev,noexec,relatime"
|
||||
home_mount_options: "defaults,nosuid,nodev,relatime"
|
||||
dev_shm_mount_options: "defaults,nosuid,nodev,noexec,relatime"
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# Remount and persist secure options using defaults variables
|
||||
|
||||
- name: Gather mount facts
|
||||
ansible.builtin.setup:
|
||||
filter: ansible_mounts
|
||||
|
||||
- name: Ensure /tmp has secure mount options
|
||||
ansible.builtin.mount:
|
||||
path: /tmp
|
||||
fstype: "{{ ansible_mounts | selectattr('mount','equalto','/tmp') | map(attribute='fstype') | first }}"
|
||||
opts: "{{ tmp_mount_options }}"
|
||||
state: mounted
|
||||
|
||||
- name: Ensure /var/tmp has secure mount options
|
||||
ansible.builtin.mount:
|
||||
path: /var/tmp
|
||||
fstype: "{{ ansible_mounts | selectattr('mount','equalto','/var/tmp') | map(attribute='fstype') | first }}"
|
||||
opts: "{{ var_tmp_mount_options }}"
|
||||
state: mounted
|
||||
|
||||
- name: Ensure /home has secure mount options
|
||||
ansible.builtin.mount:
|
||||
path: /home
|
||||
fstype: "{{ ansible_mounts | selectattr('mount','equalto','/home') | map(attribute='fstype') | first }}"
|
||||
opts: "{{ home_mount_options }}"
|
||||
state: mounted
|
||||
|
||||
- name: Ensure /dev/shm has secure mount options
|
||||
ansible.builtin.mount:
|
||||
path: /dev/shm
|
||||
fstype: tmpfs
|
||||
opts: "{{ dev_shm_mount_options }}"
|
||||
state: mounted
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
- name: restart rsyslog
|
||||
ansible.builtin.service:
|
||||
name: rsyslog
|
||||
state: restarted
|
||||
|
||||
- name: restart auditd
|
||||
ansible.builtin.service:
|
||||
name: auditd
|
||||
state: restarted
|
||||
|
||||
- name: reload systemd
|
||||
ansible.builtin.command: systemctl daemon-reexec
|
||||
|
||||
- name: update initramfs
|
||||
ansible.builtin.command: dracut -f
|
||||
when: ansible_facts['os_family'] == 'RedHat' or ansible_facts['distribution'] in ['Rocky','RedHat','CentOS']
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# Kernel defaults (override per host if needed)
|
||||
modprobe_blacklist_file: /etc/modprobe.d/cis_blacklist.conf
|
||||
|
||||
# Additional kernel modules to disable
|
||||
blacklist_modules:
|
||||
- tipc
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# blacklist_module.yml
|
||||
- name: Check if module {{ mod }} exists on system
|
||||
ansible.builtin.shell: "modprobe -n -v {{ mod }}"
|
||||
register: modprobe_check
|
||||
ignore_errors: true
|
||||
|
||||
- name: Blacklist module {{ mod }} in {{ modprobe_blacklist_file }}
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ modprobe_blacklist_file }}"
|
||||
line: "install {{ mod }} /bin/false"
|
||||
create: yes
|
||||
state: present
|
||||
when: modprobe_check.rc == 0
|
||||
|
||||
- name: Unload module {{ mod }} if currently loaded
|
||||
ansible.builtin.shell: "modprobe -r {{ mod }}"
|
||||
ignore_errors: true
|
||||
when: modprobe_check.rc == 0
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
---
|
||||
# Kernel & sysctl hardening (CIS network/kernel items)
|
||||
|
||||
- name: Ensure IPv4 forwarding is disabled
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure IPv6 forwarding is disabled
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv6.conf.all.forwarding
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure source routed packets are not accepted
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv4.conf.all.accept_source_route
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure ICMP redirects are not accepted
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv4.conf.all.accept_redirects
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure secure ICMP redirects are not accepted
|
||||
ansible.builtin.sysctl:
|
||||
name: net.ipv4.conf.all.secure_redirects
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure Address Space Layout Randomization (ASLR) is enabled
|
||||
ansible.builtin.sysctl:
|
||||
name: kernel.randomize_va_space
|
||||
value: "2"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Ensure fs.suid_dumpable is 0 (disable core dumps)
|
||||
ansible.builtin.sysctl:
|
||||
name: fs.suid_dumpable
|
||||
value: "0"
|
||||
state: present
|
||||
reload: yes
|
||||
|
||||
- name: Create /etc/modprobe.d/cis_blacklist.conf with common blacklists
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/modprobe.d/cis_blacklist.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
content: |
|
||||
# CIS blacklist
|
||||
install cramfs /bin/true
|
||||
install freevxfs /bin/true
|
||||
install jffs2 /bin/true
|
||||
install hfs /bin/true
|
||||
install hfsplus /bin/true
|
||||
install squashfs /bin/true
|
||||
install udf /bin/true
|
||||
|
||||
- name: Regenerate initramfs if modprobe blacklist changed
|
||||
ansible.builtin.command: dracut -f
|
||||
changed_when: false
|
||||
when: ansible_facts.get('os_family') == 'RedHat' or ansible_facts.get('distribution') in ['Rocky', 'RedHat', 'CentOS']
|
||||
|
||||
- name: Blacklist all modules
|
||||
include_tasks: blacklist_module.yml
|
||||
loop: "{{ blacklist_modules }}"
|
||||
loop_control:
|
||||
loop_var: mod
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# Password policy defaults
|
||||
pw_minlen: 14
|
||||
pw_minclass: 4
|
||||
pw_max_days: 365
|
||||
pw_min_days: 7
|
||||
pw_warn_age: 7
|
||||
faillock_deny: 5
|
||||
faillock_unlock_time: 900
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
# Password and PAM related hardening
|
||||
|
||||
- name: Ensure password hashing algorithm is SHA512
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/login.defs
|
||||
regexp: "^ENCRYPT_METHOD"
|
||||
line: "ENCRYPT_METHOD SHA512"
|
||||
create: yes
|
||||
|
||||
- name: Ensure PASS_MAX_DAYS is 365
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/login.defs
|
||||
regexp: "^PASS_MAX_DAYS"
|
||||
line: "PASS_MAX_DAYS 365"
|
||||
create: yes
|
||||
|
||||
- name: Ensure PASS_MIN_DAYS is 7
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/login.defs
|
||||
regexp: "^PASS_MIN_DAYS"
|
||||
line: "PASS_MIN_DAYS 7"
|
||||
create: yes
|
||||
|
||||
- name: Ensure PASS_WARN_AGE is 7
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/login.defs
|
||||
regexp: "^PASS_WARN_AGE"
|
||||
line: "PASS_WARN_AGE 7"
|
||||
create: yes
|
||||
|
||||
- name: Ensure pwquality minlen and requirements
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/security/pwquality.conf
|
||||
regexp: "^minlen"
|
||||
line: "minlen = 14"
|
||||
create: yes
|
||||
|
||||
- name: Ensure pwquality minclass (character classes) is 4
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/security/pwquality.conf
|
||||
regexp: "^minclass"
|
||||
line: "minclass = 4"
|
||||
create: yes
|
||||
|
||||
- name: Ensure pam_faillock preauth is configured
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/pam.d/system-auth
|
||||
insertafter: "^auth.*required.*pam_unix.so"
|
||||
line: "auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900"
|
||||
state: present
|
||||
backrefs: no
|
||||
|
||||
- name: Ensure pam_faillock authfail/account lines exist
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/pam.d/system-auth
|
||||
insertafter: "^auth.*required.*pam_unix.so"
|
||||
block: |
|
||||
auth [default=die] pam_faillock.so authfail deny=5 unlock_time=900 audit
|
||||
account required pam_faillock.so
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
---
|
||||
unused_services:
|
||||
- avahi-daemon
|
||||
- bluetooth
|
||||
- cups
|
||||
- rpcbind
|
||||
- nfslock
|
||||
- rsh.socket
|
||||
- telnet.socket
|
||||
- rsyncd
|
||||
@@ -0,0 +1,10 @@
|
||||
- name: Ensure list of unnecessary services are stopped and masked
|
||||
ansible.builtin.service:
|
||||
name: "{{ svc }}"
|
||||
enabled: no
|
||||
state: stopped
|
||||
masked: yes
|
||||
loop: "{{ unused_services }}"
|
||||
loop_control:
|
||||
loop_var: svc
|
||||
ignore_errors: true
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
---
|
||||
# General settings
|
||||
ssh_protocol: 2
|
||||
ssh_permit_root_login: "no"
|
||||
ssh_password_authentication: "no"
|
||||
ssh_challenge_response_authentication: "no"
|
||||
ssh_kerberos_authentication: "no"
|
||||
ssh_gssapi_authentication: "no"
|
||||
ssh_use_pam: "yes"
|
||||
PrintLastLog: "yes"
|
||||
|
||||
# Session security
|
||||
ssh_x11_forwarding: "no"
|
||||
ssh_allow_tcp_forwarding: "yes"
|
||||
ssh_permit_tunnel: "no"
|
||||
ssh_allow_agent_forwarding: "no"
|
||||
|
||||
# Idle timeout / login attempts
|
||||
ssh_client_alive_interval: 300
|
||||
ssh_client_alive_count_max: 3
|
||||
ssh_login_grace_time: 60
|
||||
ssh_max_auth_tries: 3
|
||||
ssh_max_sessions: 2
|
||||
ssh_max_startups: "10:30:60"
|
||||
|
||||
# SSH access restrictions
|
||||
ssh_allow_users: "adminuser ansibleuser"
|
||||
|
||||
# Strong encryption
|
||||
ssh_ciphers: "aes256-ctr,aes192-ctr,aes128-ctr"
|
||||
ssh_macs: "hmac-sha2-512,hmac-sha2-256"
|
||||
ssh_kex_algorithms: "[email protected],diffie-hellman-group-exchange-sha256"
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
- name: Ensure OpenSSH server is installed
|
||||
ansible.builtin.package:
|
||||
name: openssh-server
|
||||
state: present
|
||||
|
||||
- name: Ensure SSH daemon is enabled and running
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
enabled: true
|
||||
state: started
|
||||
|
||||
- name: Backup current sshd_config
|
||||
ansible.builtin.copy:
|
||||
src: /etc/ssh/sshd_config
|
||||
dest: /etc/ssh/sshd_config.bak
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
remote_src: true
|
||||
|
||||
- name: Harden SSH configuration
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "{{ ssh_item.regexp }}"
|
||||
line: "{{ ssh_item.line }}"
|
||||
state: present
|
||||
backrefs: yes
|
||||
loop:
|
||||
- regexp: "^#?Protocol"
|
||||
line: "Protocol {{ ssh_protocol }}"
|
||||
- regexp: "^#?PermitRootLogin"
|
||||
line: "PermitRootLogin {{ ssh_permit_root_login }}"
|
||||
- regexp: "^#?PasswordAuthentication"
|
||||
line: "PasswordAuthentication {{ ssh_password_authentication }}"
|
||||
- regexp: "^#?ChallengeResponseAuthentication"
|
||||
line: "ChallengeResponseAuthentication {{ ssh_challenge_response_authentication }}"
|
||||
- regexp: "^#?KerberosAuthentication"
|
||||
line: "KerberosAuthentication {{ ssh_kerberos_authentication }}"
|
||||
- regexp: "^#?GSSAPIAuthentication"
|
||||
line: "GSSAPIAuthentication {{ ssh_gssapi_authentication }}"
|
||||
- regexp: "^#?UsePAM"
|
||||
line: "UsePAM {{ ssh_use_pam }}"
|
||||
- regexp: "^#?X11Forwarding"
|
||||
line: "X11Forwarding {{ ssh_x11_forwarding }}"
|
||||
- regexp: "^#?AllowTcpForwarding"
|
||||
line: "AllowTcpForwarding {{ ssh_allow_tcp_forwarding }}"
|
||||
- regexp: "^#?PermitTunnel"
|
||||
line: "PermitTunnel {{ ssh_permit_tunnel }}"
|
||||
- regexp: "^#?AllowAgentForwarding"
|
||||
line: "AllowAgentForwarding {{ ssh_allow_agent_forwarding }}"
|
||||
- regexp: "^#?ClientAliveInterval"
|
||||
line: "ClientAliveInterval {{ ssh_client_alive_interval }}"
|
||||
- regexp: "^#?ClientAliveCountMax"
|
||||
line: "ClientAliveCountMax {{ ssh_client_alive_count_max }}"
|
||||
- regexp: "^#?LoginGraceTime"
|
||||
line: "LoginGraceTime {{ ssh_login_grace_time }}"
|
||||
- regexp: "^#?MaxAuthTries"
|
||||
line: "MaxAuthTries {{ ssh_max_auth_tries }}"
|
||||
- regexp: "^#?MaxSessions"
|
||||
line: "MaxSessions {{ ssh_max_sessions }}"
|
||||
- regexp: "^#?MaxStartups"
|
||||
line: "MaxStartups {{ ssh_max_startups }}"
|
||||
- regexp: "^#?Ciphers"
|
||||
line: "Ciphers {{ ssh_ciphers }}"
|
||||
- regexp: "^#?MACs"
|
||||
line: "MACs {{ ssh_macs }}"
|
||||
- regexp: "^#?KexAlgorithms"
|
||||
line: "KexAlgorithms {{ ssh_kex_algorithms }}"
|
||||
- regexp: "^#?PrintLastLog"
|
||||
line: "PrintLastLog {{ PrintLastLog }}"
|
||||
- regexp: '^\s*#?\s*AllowUsers'
|
||||
line: "AllowUsers {{ ssh_allow_users }}"
|
||||
- regexp: "^#?Banner"
|
||||
line: "Banner /etc/issue.net"
|
||||
loop_control:
|
||||
loop_var: ssh_item
|
||||
|
||||
- name: Restart SSH service to apply changes
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: restarted
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# Parent CIS hardening role: import each subrole with single tag
|
||||
- import_role:
|
||||
name: cis_hardening/kernel_hardening
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/password_policy
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/file_permissions
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/service_disable
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/banners
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/ssh_hardening
|
||||
tags: [cis_hardening]
|
||||
|
||||
- import_role:
|
||||
name: cis_hardening/filesystem_hardening
|
||||
tags: [cis_hardening]
|
||||
Reference in new issue
Block a user