Add RHEL 9 CIS-inspired hardening playbook
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
[defaults]
|
||||
inventory = inventory/hosts.yml
|
||||
roles_path = roles
|
||||
host_key_checking = False
|
||||
retry_files_enabled = False
|
||||
stdout_callback = yaml
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
- name: community.general
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
timezone: UTC
|
||||
|
||||
cis_ntp_servers:
|
||||
- 0.rhel.pool.ntp.org
|
||||
- 1.rhel.pool.ntp.org
|
||||
- 2.rhel.pool.ntp.org
|
||||
- 3.rhel.pool.ntp.org
|
||||
|
||||
# Operational defaults. Override per run with --extra-vars or inventory when needed.
|
||||
cis_disable_root_login: true
|
||||
cis_disable_password_auth: false
|
||||
cis_install_auditd: true
|
||||
cis_enable_chrony: true
|
||||
cis_enable_rsyslog: true
|
||||
cis_remove_legacy_packages: true
|
||||
cis_enable_sysctl_hardening: true
|
||||
cis_manage_mount_options: false
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
linux:
|
||||
hosts:
|
||||
localhost:
|
||||
ansible_connection: local
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
- name: Apply CIS-inspired RHEL 9 hardening controls
|
||||
hosts: linux
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- role: cis-rhel9-hardening
|
||||
tags:
|
||||
- cis
|
||||
- hardening
|
||||
|
||||
post_tasks:
|
||||
- name: Show validation summary
|
||||
ansible.builtin.debug:
|
||||
var: cis_validation_summary
|
||||
when: cis_validation_summary is defined
|
||||
tags:
|
||||
- always
|
||||
- postcheck
|
||||
@@ -0,0 +1,83 @@
|
||||
# CIS-Inspired RHEL 9 Hardening Role
|
||||
|
||||
This role provides a practical, production-style hardening baseline for RHEL 9 and Oracle Linux 9 systems. It is inspired by CIS Benchmark controls for Red Hat Enterprise Linux 9 version 2.0.0, but it is intentionally scoped to common operational controls that infrastructure and security operations teams frequently automate.
|
||||
|
||||
This is not a full CIS certification implementation.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
- Red Hat Enterprise Linux 9
|
||||
- Oracle Linux 9
|
||||
|
||||
The role fails safely on unsupported operating systems or unsupported major versions.
|
||||
|
||||
## Implemented Controls
|
||||
|
||||
- SSH daemon hardening for root login, empty passwords, password authentication, retry limits, login grace time, and client keepalive behavior.
|
||||
- Removal of selected legacy network packages such as telnet, rsh-server, and ypbind.
|
||||
- Optional installation and enablement of chrony, auditd, and rsyslog.
|
||||
- CIS-inspired IPv4 network sysctl settings.
|
||||
- Service enablement for chronyd, auditd, and rsyslog.
|
||||
- Safe disabling of known legacy services when they are present.
|
||||
- Basic audit backlog and audit rule examples.
|
||||
- Sudo defaults for `use_pty` and a configurable sudo logfile.
|
||||
- Rsyslog service validation and journald configuration presence checks.
|
||||
- Optional filesystem mount option persistence for selected paths.
|
||||
|
||||
## Safety Philosophy
|
||||
|
||||
The defaults are conservative. The role supports Ansible check mode and avoids destructive production behavior by default. Filesystem mount option management is disabled unless `cis_manage_mount_options` is explicitly enabled, and even then the role persists configured options without remounting live filesystems.
|
||||
|
||||
Review variables before using this role in production.
|
||||
|
||||
## Common Variables
|
||||
|
||||
```yaml
|
||||
cis_disable_root_login: true
|
||||
cis_disable_password_auth: false
|
||||
cis_install_auditd: true
|
||||
cis_enable_chrony: true
|
||||
cis_enable_rsyslog: true
|
||||
cis_remove_legacy_packages: true
|
||||
cis_enable_sysctl_hardening: true
|
||||
cis_manage_mount_options: false
|
||||
```
|
||||
|
||||
## Check Mode
|
||||
|
||||
Run a full safety preview:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/cis-rhel9-hardening.yml --check --diff
|
||||
```
|
||||
|
||||
Run only SSH controls in check mode:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/cis-rhel9-hardening.yml --check --diff --tags ssh
|
||||
```
|
||||
|
||||
## Tags
|
||||
|
||||
Useful tags include:
|
||||
|
||||
- `precheck`
|
||||
- `packages`
|
||||
- `ssh`
|
||||
- `sysctl`
|
||||
- `services`
|
||||
- `audit`
|
||||
- `sudo`
|
||||
- `logging`
|
||||
- `filesystem`
|
||||
- `postcheck`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/cis-rhel9-hardening.yml --tags precheck,ssh,postcheck
|
||||
```
|
||||
|
||||
## Production Rollout Notes
|
||||
|
||||
This role is a hardening starting point for internal infrastructure teams. It should be reviewed against local access patterns, break-glass procedures, compliance requirements, monitoring expectations, and host build standards before rollout.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
cis_benchmark_version: "2.0.0"
|
||||
|
||||
cis_disable_root_login: true
|
||||
cis_disable_password_auth: false
|
||||
cis_install_auditd: true
|
||||
cis_enable_chrony: true
|
||||
cis_enable_rsyslog: true
|
||||
cis_remove_legacy_packages: true
|
||||
cis_enable_sysctl_hardening: true
|
||||
cis_manage_mount_options: false
|
||||
|
||||
cis_ssh_max_auth_tries: 4
|
||||
cis_ssh_login_grace_time: 60
|
||||
cis_ssh_client_alive_interval: 300
|
||||
cis_ssh_client_alive_count_max: 3
|
||||
cis_ssh_dropin_path: /etc/ssh/sshd_config.d/50-cis-rhel9-hardening.conf
|
||||
|
||||
cis_min_root_free_mb: 1024
|
||||
|
||||
cis_legacy_packages:
|
||||
- telnet
|
||||
- rsh-server
|
||||
- ypbind
|
||||
|
||||
cis_legacy_services:
|
||||
- telnet.socket
|
||||
- rsh.socket
|
||||
- rexec.socket
|
||||
- rlogin.socket
|
||||
- ypbind.service
|
||||
|
||||
cis_sysctl_settings:
|
||||
net.ipv4.ip_forward: 0
|
||||
net.ipv4.conf.all.send_redirects: 0
|
||||
net.ipv4.conf.default.send_redirects: 0
|
||||
net.ipv4.conf.all.accept_source_route: 0
|
||||
net.ipv4.conf.default.accept_source_route: 0
|
||||
net.ipv4.conf.all.accept_redirects: 0
|
||||
net.ipv4.conf.default.accept_redirects: 0
|
||||
net.ipv4.tcp_syncookies: 1
|
||||
|
||||
cis_sysctl_config_file: /etc/sysctl.d/60-cis-rhel9-hardening.conf
|
||||
|
||||
cis_audit_rules_path: /etc/audit/rules.d/50-cis-rhel9-hardening.rules
|
||||
cis_audit_backlog_limit: 8192
|
||||
cis_audit_rules:
|
||||
- "-w /etc/passwd -p wa -k identity"
|
||||
- "-w /etc/shadow -p wa -k identity"
|
||||
- "-w /etc/group -p wa -k identity"
|
||||
- "-w /etc/gshadow -p wa -k identity"
|
||||
- "-w /etc/sudoers -p wa -k scope"
|
||||
- "-w /etc/sudoers.d/ -p wa -k scope"
|
||||
- "-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change"
|
||||
|
||||
cis_sudoers_dropin_path: /etc/sudoers.d/50-cis-rhel9-hardening
|
||||
cis_sudo_logfile: /var/log/sudo.log
|
||||
|
||||
cis_mount_option_targets:
|
||||
- path: /tmp
|
||||
options:
|
||||
- nodev
|
||||
- nosuid
|
||||
- noexec
|
||||
- path: /var/tmp
|
||||
options:
|
||||
- nodev
|
||||
- nosuid
|
||||
- noexec
|
||||
- path: /home
|
||||
options:
|
||||
- nodev
|
||||
|
||||
cis_container_virtualization_types:
|
||||
- container
|
||||
- docker
|
||||
- lxc
|
||||
- podman
|
||||
- containerd
|
||||
- systemd-nspawn
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Validate sshd configuration
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
listen: validate sshd
|
||||
|
||||
- name: Reload sshd
|
||||
ansible.builtin.service:
|
||||
name: sshd
|
||||
state: reloaded
|
||||
listen: reload sshd
|
||||
|
||||
- name: Restart auditd
|
||||
ansible.builtin.service:
|
||||
name: auditd
|
||||
state: restarted
|
||||
use: service
|
||||
listen: restart auditd
|
||||
|
||||
- name: Restart rsyslog
|
||||
ansible.builtin.service:
|
||||
name: rsyslog
|
||||
state: restarted
|
||||
listen: restart rsyslog
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
- name: Ensure audit rules directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/audit/rules.d
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0750"
|
||||
|
||||
- name: Configure audit backlog limit
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/audit/audit.rules
|
||||
regexp: '^-b\s+'
|
||||
line: "-b {{ cis_audit_backlog_limit }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0640"
|
||||
notify: restart auditd
|
||||
|
||||
- name: Install baseline audit rules
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_audit_rules_path }}"
|
||||
line: "{{ item }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0640"
|
||||
loop: "{{ cis_audit_rules }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
notify: restart auditd
|
||||
|
||||
- name: Ensure auditd is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
name: auditd
|
||||
enabled: true
|
||||
state: started
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
- name: Gather current mount facts
|
||||
ansible.builtin.set_fact:
|
||||
cis_current_mount_paths: "{{ ansible_mounts | map(attribute='mount') | list }}"
|
||||
|
||||
- name: Report filesystem mount option mode
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ 'OK: Mount option management is enabled for configured targets.'
|
||||
if cis_manage_mount_options | bool
|
||||
else 'WARNING: Mount option management is disabled. No production filesystems will be remounted.' }}
|
||||
|
||||
- name: Show configured mount option recommendations
|
||||
ansible.builtin.debug:
|
||||
msg: "Review {{ item.path }} for options: {{ item.options | join(',') }}"
|
||||
loop: "{{ cis_mount_option_targets }}"
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
when: not cis_manage_mount_options | bool
|
||||
|
||||
- name: Persist configured mount options without remounting
|
||||
ansible.posix.mount:
|
||||
path: "{{ item.path }}"
|
||||
src: "{{ cis_mount_fact.device }}"
|
||||
fstype: "{{ cis_mount_fact.fstype }}"
|
||||
state: present
|
||||
opts: "{{ ((cis_mount_fact.options | default('defaults')).split(',') + item.options) | unique | join(',') }}"
|
||||
loop: "{{ cis_mount_option_targets }}"
|
||||
loop_control:
|
||||
label: "{{ item.path }}"
|
||||
vars:
|
||||
cis_mount_fact: "{{ ansible_mounts | selectattr('mount', 'equalto', item.path) | list | first | default({}) }}"
|
||||
when:
|
||||
- cis_manage_mount_options | bool
|
||||
- item.path in cis_current_mount_paths
|
||||
register: cis_mount_option_results
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Ensure rsyslog is installed
|
||||
ansible.builtin.package:
|
||||
name: rsyslog
|
||||
state: present
|
||||
when: cis_enable_rsyslog | bool
|
||||
|
||||
- name: Ensure rsyslog is enabled and running
|
||||
ansible.builtin.systemd:
|
||||
name: rsyslog
|
||||
enabled: true
|
||||
state: started
|
||||
when: cis_enable_rsyslog | bool
|
||||
|
||||
- name: Validate journald configuration file presence
|
||||
ansible.builtin.stat:
|
||||
path: /etc/systemd/journald.conf
|
||||
register: cis_journald_conf
|
||||
|
||||
- name: Report journald configuration status
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ 'OK: /etc/systemd/journald.conf is present.'
|
||||
if cis_journald_conf.stat.exists else 'WARNING: /etc/systemd/journald.conf was not found.' }}
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
- name: Run platform safety prechecks
|
||||
ansible.builtin.import_tasks: precheck.yml
|
||||
tags:
|
||||
- always
|
||||
- precheck
|
||||
|
||||
- name: Manage packages
|
||||
ansible.builtin.import_tasks: packages.yml
|
||||
tags:
|
||||
- packages
|
||||
|
||||
- name: Harden SSH daemon configuration
|
||||
ansible.builtin.import_tasks: ssh.yml
|
||||
tags:
|
||||
- ssh
|
||||
|
||||
- name: Apply kernel network hardening
|
||||
ansible.builtin.import_tasks: sysctl.yml
|
||||
when: cis_enable_sysctl_hardening | bool
|
||||
tags:
|
||||
- sysctl
|
||||
|
||||
- name: Manage baseline services
|
||||
ansible.builtin.import_tasks: services.yml
|
||||
tags:
|
||||
- services
|
||||
|
||||
- name: Configure Linux audit controls
|
||||
ansible.builtin.import_tasks: audit.yml
|
||||
when: cis_install_auditd | bool
|
||||
tags:
|
||||
- audit
|
||||
|
||||
- name: Configure sudo controls
|
||||
ansible.builtin.import_tasks: sudo.yml
|
||||
tags:
|
||||
- sudo
|
||||
|
||||
- name: Configure logging controls
|
||||
ansible.builtin.import_tasks: logging.yml
|
||||
tags:
|
||||
- logging
|
||||
|
||||
- name: Review filesystem mount options
|
||||
ansible.builtin.import_tasks: filesystem.yml
|
||||
tags:
|
||||
- filesystem
|
||||
|
||||
- name: Run validation postchecks
|
||||
ansible.builtin.import_tasks: postcheck.yml
|
||||
tags:
|
||||
- always
|
||||
- postcheck
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: Remove legacy network packages
|
||||
ansible.builtin.package:
|
||||
name: "{{ cis_legacy_packages }}"
|
||||
state: absent
|
||||
when: cis_remove_legacy_packages | bool
|
||||
|
||||
- name: Install chrony when enabled
|
||||
ansible.builtin.package:
|
||||
name: chrony
|
||||
state: present
|
||||
when: cis_enable_chrony | bool
|
||||
|
||||
- name: Install auditd when enabled
|
||||
ansible.builtin.package:
|
||||
name: audit
|
||||
state: present
|
||||
when: cis_install_auditd | bool
|
||||
|
||||
- name: Install rsyslog when enabled
|
||||
ansible.builtin.package:
|
||||
name: rsyslog
|
||||
state: present
|
||||
when: cis_enable_rsyslog | bool
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
- name: Validate sshd effective configuration syntax
|
||||
ansible.builtin.command: sshd -t
|
||||
register: cis_sshd_validate
|
||||
changed_when: false
|
||||
check_mode: false
|
||||
|
||||
- name: Read sysctl values for validation
|
||||
ansible.builtin.command: "sysctl -n {{ item.key }}"
|
||||
loop: "{{ cis_sysctl_settings | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
register: cis_sysctl_validation
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
check_mode: false
|
||||
when: cis_enable_sysctl_hardening | bool
|
||||
|
||||
- name: Gather final service facts
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Build service state summary
|
||||
ansible.builtin.set_fact:
|
||||
cis_service_state_summary:
|
||||
chronyd: "{{ ansible_facts.services['chronyd.service'].state | default('not-found') }}"
|
||||
auditd: "{{ ansible_facts.services['auditd.service'].state | default('not-found') }}"
|
||||
rsyslog: "{{ ansible_facts.services['rsyslog.service'].state | default('not-found') }}"
|
||||
|
||||
- name: Build sysctl validation summary
|
||||
ansible.builtin.set_fact:
|
||||
cis_sysctl_validation_summary: "{{ cis_sysctl_validation_summary | default({}) | combine({item.item.key: item.stdout | default('unreadable')}) }}"
|
||||
loop: "{{ cis_sysctl_validation.results | default([]) }}"
|
||||
loop_control:
|
||||
label: "{{ item.item.key }}"
|
||||
when: cis_enable_sysctl_hardening | bool
|
||||
|
||||
- name: Build mount option change summary
|
||||
ansible.builtin.set_fact:
|
||||
cis_mount_option_summary: >-
|
||||
{{
|
||||
cis_mount_option_results.results
|
||||
| default([])
|
||||
| selectattr('changed', 'defined')
|
||||
| selectattr('changed')
|
||||
| map(attribute='item.path')
|
||||
| list
|
||||
}}
|
||||
|
||||
- name: Publish validation summary
|
||||
ansible.builtin.set_fact:
|
||||
cis_validation_summary:
|
||||
benchmark: "CIS RHEL 9 Benchmark {{ cis_benchmark_version }} inspired controls"
|
||||
sshd_config: "{{ 'OK' if cis_sshd_validate.rc == 0 else 'CRITICAL' }}"
|
||||
services: "{{ cis_service_state_summary }}"
|
||||
sysctl: "{{ cis_sysctl_validation_summary | default({}) }}"
|
||||
mount_option_updates: "{{ cis_mount_option_summary | default([]) }}"
|
||||
applied_controls:
|
||||
- ssh
|
||||
- packages
|
||||
- sysctl
|
||||
- services
|
||||
- audit
|
||||
- sudo
|
||||
- logging
|
||||
- filesystem
|
||||
|
||||
- name: Show service states
|
||||
ansible.builtin.debug:
|
||||
var: cis_service_state_summary
|
||||
|
||||
- name: Show changed mount options
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ cis_mount_option_summary | default([]) if cis_mount_option_summary | default([]) | length > 0
|
||||
else 'OK: No mount option changes were applied.' }}
|
||||
|
||||
- name: Show applied control summary
|
||||
ansible.builtin.debug:
|
||||
var: cis_validation_summary
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
- name: Determine root filesystem free space
|
||||
ansible.builtin.set_fact:
|
||||
cis_root_mount: "{{ ansible_mounts | selectattr('mount', 'equalto', '/') | list | first | default({}) }}"
|
||||
|
||||
- name: Calculate root filesystem free space in MB
|
||||
ansible.builtin.set_fact:
|
||||
cis_root_free_mb: "{{ ((cis_root_mount.size_available | default(0) | int) / 1024 / 1024) | round(0, 'floor') | int }}"
|
||||
|
||||
- name: Detect containerized runtime
|
||||
ansible.builtin.set_fact:
|
||||
cis_container_detected: >-
|
||||
{{
|
||||
ansible_virtualization_type | default('') in cis_container_virtualization_types
|
||||
or ansible_env.container | default('') | length > 0
|
||||
}}
|
||||
|
||||
- name: Report platform precheck status
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "OK: Facts gathered for {{ ansible_distribution }} {{ ansible_distribution_version }}."
|
||||
- "OK: Root filesystem free space is {{ cis_root_free_mb }} MB."
|
||||
- >-
|
||||
{{ 'WARNING: Containerized environment detected; service and kernel controls may be limited.'
|
||||
if cis_container_detected else 'OK: No containerized runtime detected from Ansible facts.' }}
|
||||
- >-
|
||||
{{ 'OK: systemd service manager detected.'
|
||||
if ansible_service_mgr == 'systemd' else 'CRITICAL: systemd service manager is required.' }}
|
||||
|
||||
- name: Fail when operating system is unsupported
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_distribution in cis_supported_distributions
|
||||
- ansible_distribution_major_version == cis_supported_major_version
|
||||
fail_msg: >-
|
||||
CRITICAL: This role supports only RHEL 9 / Oracle Linux 9 compatible systems.
|
||||
Detected {{ ansible_distribution }} {{ ansible_distribution_version }}.
|
||||
success_msg: "OK: Supported RHEL 9 compatible platform detected."
|
||||
|
||||
- name: Fail when systemd is unavailable
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_service_mgr == 'systemd'
|
||||
fail_msg: "CRITICAL: systemd is required for this operational hardening role."
|
||||
success_msg: "OK: systemd is available."
|
||||
|
||||
- name: Fail when root filesystem free space is below safety threshold
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- cis_root_free_mb | int >= cis_min_root_free_mb | int
|
||||
fail_msg: >-
|
||||
CRITICAL: Root filesystem has {{ cis_root_free_mb }} MB free.
|
||||
Minimum required free space is {{ cis_min_root_free_mb }} MB.
|
||||
success_msg: "OK: Root filesystem free space meets the safety threshold."
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
- name: Enable chronyd service
|
||||
ansible.builtin.systemd:
|
||||
name: chronyd
|
||||
enabled: true
|
||||
state: started
|
||||
when: cis_enable_chrony | bool
|
||||
|
||||
- name: Enable rsyslog service
|
||||
ansible.builtin.systemd:
|
||||
name: rsyslog
|
||||
enabled: true
|
||||
state: started
|
||||
when: cis_enable_rsyslog | bool
|
||||
|
||||
- name: Enable auditd service
|
||||
ansible.builtin.systemd:
|
||||
name: auditd
|
||||
enabled: true
|
||||
state: started
|
||||
when: cis_install_auditd | bool
|
||||
|
||||
- name: Gather service facts
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Disable unnecessary legacy services when present
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: false
|
||||
state: stopped
|
||||
loop: "{{ cis_legacy_services }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
when:
|
||||
- cis_remove_legacy_packages | bool
|
||||
- item in ansible_facts.services
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
- name: Ensure sshd drop-in directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ cis_ssh_dropin_path | dirname }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Ensure sshd hardening drop-in exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
state: touch
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
modification_time: preserve
|
||||
access_time: preserve
|
||||
|
||||
- name: Configure SSH root login
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^PermitRootLogin\s+'
|
||||
line: "PermitRootLogin {{ 'no' if cis_disable_root_login | bool else 'prohibit-password' }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH empty password restriction
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^PermitEmptyPasswords\s+'
|
||||
line: "PermitEmptyPasswords no"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH password authentication
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^PasswordAuthentication\s+'
|
||||
line: "PasswordAuthentication {{ 'no' if cis_disable_password_auth | bool else 'yes' }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH MaxAuthTries
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^MaxAuthTries\s+'
|
||||
line: "MaxAuthTries {{ cis_ssh_max_auth_tries }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH LoginGraceTime
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^LoginGraceTime\s+'
|
||||
line: "LoginGraceTime {{ cis_ssh_login_grace_time }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH ClientAliveInterval
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^ClientAliveInterval\s+'
|
||||
line: "ClientAliveInterval {{ cis_ssh_client_alive_interval }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
|
||||
- name: Configure SSH ClientAliveCountMax
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_ssh_dropin_path }}"
|
||||
regexp: '^ClientAliveCountMax\s+'
|
||||
line: "ClientAliveCountMax {{ cis_ssh_client_alive_count_max }}"
|
||||
validate: sshd -t -f %s
|
||||
notify:
|
||||
- validate sshd
|
||||
- reload sshd
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
- name: Configure sudo hardening drop-in
|
||||
ansible.builtin.lineinfile:
|
||||
path: "{{ cis_sudoers_dropin_path }}"
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
create: true
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: /usr/sbin/visudo -cf %s
|
||||
loop:
|
||||
- regexp: '^Defaults\s+use_pty'
|
||||
line: "Defaults use_pty"
|
||||
- regexp: '^Defaults\s+logfile='
|
||||
line: 'Defaults logfile="{{ cis_sudo_logfile }}"'
|
||||
loop_control:
|
||||
label: "{{ item.line }}"
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Apply CIS-inspired sysctl settings
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
sysctl_file: "{{ cis_sysctl_config_file }}"
|
||||
state: present
|
||||
reload: true
|
||||
loop: "{{ cis_sysctl_settings | dict2items }}"
|
||||
loop_control:
|
||||
label: "{{ item.key }}"
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
cis_supported_distributions:
|
||||
- RedHat
|
||||
- OracleLinux
|
||||
|
||||
cis_supported_major_version: "9"
|
||||
Reference in New Issue
Block a user