No description
  • Jinja 85.6%
  • Shell 14.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-29 14:15:01 +01:00
defaults fix 2026-10-29 14:15:01 +01:00
files Initial commit 2026-10-29 12:10:57 +01:00
handlers test 2026-10-29 13:47:51 +01:00
meta first commit 2026-10-29 12:30:37 +01:00
molecule/default Initial commit 2026-10-29 12:10:57 +01:00
tasks fix 2026-10-29 14:15:01 +01:00
templates 1 2026-10-29 14:02:50 +01:00
vars Initial commit 2026-10-29 12:10:57 +01:00
.ansible-lint Initial commit 2026-10-29 12:10:57 +01:00
.gitignore Initial commit 2026-10-29 12:10:57 +01:00
.yamllint Initial commit 2026-10-29 12:10:57 +01:00
BEST_PRACTICES.md Initial commit 2026-10-29 12:10:57 +01:00
CHANGELOG.md fix 2026-10-29 14:15:01 +01:00
CODE_OF_CONDUCT.md Initial commit 2026-10-29 12:10:57 +01:00
CONTRIBUTING.md Initial commit 2026-10-29 12:10:57 +01:00
IMPROVEMENTS.md Initial commit 2026-10-29 12:10:57 +01:00
LICENSE Initial commit 2026-10-29 12:10:57 +01:00
molecule.sh Initial commit 2026-10-29 12:10:57 +01:00
README.md fix 2026-10-29 14:15:01 +01:00
requirements.txt Initial commit 2026-10-29 12:10:57 +01:00
SECURITY.md Initial commit 2026-10-29 12:10:57 +01:00

Ansible Role Hardening

A configurable Ansible role for SPIE DataSolutions, targeting Debian 13 and Rocky Linux 9/10. The hardening scope is based on the supplied Lynis 3.1.4 report for bw01 (hardening index 84, 23 suggestions).

This role provides targeted additional hardening rather than a complete CIS baseline. Existing firewall, SELinux/AppArmor, auditd, and mount settings remain managed by their existing configuration.

The role includes hardening tasks and configurable defaults. Functional validation on Debian 13 and Rocky Linux 9/10 is still required.

Quick Start

Install the role using the Git installation method below, then create a playbook based on the example in this README and an inventory for your environment:

ansible-playbook -i inventory.ini site.yml --syntax-check
ansible-playbook -i inventory.ini site.yml --check --diff --limit debian13.example.org
ansible-playbook -i inventory.ini site.yml --limit debian13.example.org

These commands are intended for use after the hardening tasks have been implemented. Test on one VM per operating system version, with console access and a second SSH session available. The example playbook uses serial: 1 to process one host at a time.

Supported Platforms

Target platforms for the hardening implementation:

  • Debian
    • Debian 13 (Trixie)
  • Enterprise Linux
    • Rocky Linux 9
    • Rocky Linux 10

Functional validation on these platforms is still required.

Requirements

  • ansible-core >= 2.16 for the proposed implementation
  • Python on target hosts
  • systemd and a working OpenSSH server
  • sudo/become privileges
  • Docker for local testing with Molecule
  • Python packages: see requirements.txt

Installation

From Git

ansible-galaxy role install \
  git+https://git.ds.spie-nl.com/datasolutions/ansible-role-hardening.git,main,ansible-role-hardening

From Galaxy

A Galaxy release is not documented yet. Use the Git installation method above.

Role Variables

Default values belong in defaults/main.yml. The following configuration illustrates the proposed variables; these have not yet been implemented. Override them in your playbook or inventory, for example in group_vars/all.yml:

lynis_hardening_fail2ban: true
# Optionally seed jail.local once, without replacing an existing file.
lynis_hardening_fail2ban_jail_local: true
lynis_hardening_remote_logging: true
lynis_hardening_log_host: logs.example.org
lynis_hardening_log_port: 6514
# Debian: use the existing CA bundle or your own CA.
lynis_hardening_log_ca: /etc/ssl/certs/ca-certificates.crt

# Select existing accounts explicitly; missing accounts cause an error.
lynis_hardening_accounts:
  - name: administrator
    password_expire_min: 1
    password_expire_max: 365
    password_expire_warn: 14
    # Optional: absolute UNIX timestamp in seconds; -1 means no expiry.
    # expires: 1830211200

lynis_hardening_aide: true
lynis_hardening_aide_paths:
  - /etc
lynis_hardening_aide_timer: true
lynis_hardening_aide_on_calendar: '*-*-* 05:00:00'
lynis_hardening_aide_timer_persistent: true

# Only select services that have been explicitly tested.
lynis_hardening_assessment: true
# Optional detailed reports; use actual service unit names on your hosts.
lynis_hardening_assessment_services: []
lynis_hardening_disable_services: []
lynis_hardening_service_overrides: {}
# Example: test against your own application first.
# lynis_hardening_service_overrides:
#   my-app.service: |
#     NoNewPrivileges=yes
#     PrivateTmp=yes
#     ProtectSystem=full

# Explicit compiler paths; missing files are skipped and symlinks are followed.
lynis_hardening_compiler_paths:
  - /usr/bin/gcc
  - /usr/bin/g++

lynis_hardening_sysctl: {}

Overriding a dictionary replaces the entire default dictionary. To change one SSH option, include the other settings you want to retain. Setting a feature to false stops managing it; it does not undo previously applied configuration.

For variable documentation, use:

  • defaults/main.yml - Default configuration values
  • vars/main.yml - Internal role variables

Default Hardening

  • System account shells: lynis_hardening_system_accounts_nologin: true assigns lynis_hardening_system_accounts_shell: /usr/sbin/nologin to local accounts with 0 < UID < lynis_hardening_system_accounts_uid_max (default 1000). Accounts are read directly from /etc/passwd, avoiding directory-service users. UID-0 accounts, configured connection user (ansible_user/ansible_ssh_user), and lynis_hardening_system_accounts_excluded_users (default root) are skipped. lynis_hardening_system_accounts_excluded_shells preserves /sbin/shutdown, /sbin/halt, and /bin/sync. Add application service accounts that need shell execution to the exclusions before deployment; this change can affect jobs that invoke a service user's login shell. Passwords and lock states are not changed. Select with no_shelllogin_for_systemaccounts.

  • Sysctl after network startup: lynis_hardening_sysctl_reapply_after_network: true installs and enables /etc/systemd/system/sysctl-reapply-network.service on Debian. The oneshot unit runs /sbin/sysctl --system after networking.service and systemd-networkd.service, with DefaultDependencies=no and WantedBy=multi-user.target. Deployment reloads systemd when the unit changes but does not start it immediately. The next boot reapplies all host sysctl configuration, not only values managed by this role; review those files accordingly. This orders boot-time execution and does not rerun automatically for later interface hotplug or reconnect events. Check mode renders the unit but skips enablement. Select with sysctl_reapply_after_network.

  • Kernel pointer exposure: lynis_hardening_kptr_restrict: true persists and immediately applies lynis_hardening_kptr_restrict_value: '1' through /etc/sysctl.d/90-lynis-hardening.conf. An explicit kernel.kptr_restrict entry in lynis_hardening_sysctl takes precedence. Conflicting dot/slash assignments in /etc/sysctl.conf and .conf files under /etc/sysctl.d, /run/sysctl.d, /usr/local/lib/sysctl.d, and /usr/lib/sysctl.d are commented out with backups, following symlinks. Correct assignments are retained. Package/runtime configuration may be regenerated; reapply the role afterward. Select with sysctl_kernel_kptr_restrict. No automatic reboot is performed; runtime changes require permission to modify the host kernel setting.

  • Shadow backup permissions: lynis_hardening_shadow_backup_permissions: true restricts an existing regular /etc/shadow- file to mode 0000 using u-xwrs,g-xwrs,o-xwrt. Missing files and symlinks are skipped; contents and ownership are unchanged. Select the tasks with file_permissions_backup_etc_shadow.

  • Gshadow backup permissions: lynis_hardening_gshadow_backup_permissions: true removes all access and special permission bits from an existing regular /etc/gshadow- file using u-xwrs,g-xwrs,o-xwrt, resulting in mode 0000. Missing files and symlinks are skipped. This is independent of group ownership management and does not change file contents or ownership. Select it with file_permissions_backup_etc_gshadow.

  • Gshadow backup group: lynis_hardening_gshadow_backup_group: true sets the group of an existing regular /etc/gshadow- file to lynis_hardening_gshadow_backup_gid: '0' (root). Missing files and symlinks are skipped; contents, owner, and permission bits are not managed by this task. Select it with file_groupowner_backup_etc_gshadow.

  • CAN kernel module: lynis_hardening_disable_can: true manages /etc/modprobe.d/can.conf with install can /bin/false and blacklist can, with backups of existing configuration. This addresses kernel_module_can_disabled for future modprobe loading. An already loaded module is not forcibly unloaded; plan a reboot if it is active. Built-in kernel support and copies of configuration in an existing initramfs require separate review. Select the tasks with kernel_module_can_disabled.

  • ATM kernel module: lynis_hardening_disable_atm: true manages /etc/modprobe.d/atm.conf with install atm /bin/false and blacklist atm, with backups of existing configuration. This addresses kernel_module_atm_disabled for future modprobe loading. An already loaded module is not forcibly unloaded; plan a reboot if it is active. Built-in kernel support and copies of configuration in an existing initramfs require separate review. Select the tasks with kernel_module_atm_disabled.

  • Idle shell timeout: lynis_hardening_shell_timeout: true sets lynis_hardening_shell_timeout_seconds: 900 through /etc/profile.d/tmout.sh. Interactive shells loading the profile receive an exported readonly TMOUT; repeated sourcing at the configured value is supported. Standalone numeric assignments in /etc/profile are aligned without replacing arbitrary shell logic. Configuration edits use backups and /bin/sh -n validation. Review other profile files for conflicting readonly assignments. This applies to new interactive sessions in shells supporting TMOUT, such as Bash; it does not terminate active commands or enforce timeouts in shells that do not load the profile. Select the tasks with accounts_tmout.

  • Bash umask: lynis_hardening_bash_umask: true sets the quoted lynis_hardening_user_umask: '027' in /etc/bash.bashrc on Debian and /etc/bashrc on Rocky when Bash is installed. Existing standalone numeric umask commands are updated while preserving indentation and inline comments; a managed default block is placed at the end. Both edits use backups and bash -n validation. This affects new Bash sessions that read these files, typically creating files with at most 0640 and directories with at most 0750. It does not change existing files or enforce a mask against later user/application overrides. Select the tasks with accounts_umask_etc_bashrc. SSH explicitly rejects empty passwords through PermitEmptyPasswords no, separately from the PAM policy. If your inventory overrides lynis_hardening_ssh_options, include PermitEmptyPasswords: 'no' in that dictionary as well. Verify the effective value with sshd -T and, for applicable Match blocks, sshd -T -C user=USER,host=HOST,addr=CLIENT_IP.

  • Legacy clients: lynis_hardening_remove_inetutils_telnet: true removes the inetutils-telnet package. This targets that package specifically, not every distribution's telnet client package. Disabling the variable does not reinstall it. Select the task with package_inetutils-telnet_removed.

  • Time synchronization: lynis_hardening_timesyncd: true configures hosts where the systemd-timesyncd package is already installed. Primary servers default to 0.debian.pool.ntp.org and 1.debian.pool.ntp.org; fallback servers default to 2.debian.pool.ntp.org and 3.debian.pool.ntp.org. Override lynis_hardening_timesyncd_ntp_servers and lynis_hardening_timesyncd_fallback_servers with lists of approved hostnames/IP addresses. The managed /etc/systemd/timesyncd.conf.d/oscap-remedy.conf resets previously accumulated lists before setting these values, with backups and restart on change. Review later drop-ins and per-link/DHCP time servers separately. This does not install timesyncd, enable it at boot, or replace chrony/ntpd; hosts without the package are skipped. Verify synchronization after deployment using timedatectl timesync-status. Select the tasks with service_timesyncd_configured.

  • Yearly cron directory: lynis_hardening_cron_yearly_permissions: true restricts the existing /etc/cron.yearly directory to 0700 or less permissive using u-s,g-xwrs,o-xwrt. Stricter permissions and ownership are preserved. Missing directories and symlinks are skipped; files inside the directory are not changed. Select the tasks with file_permissions_cron_yearly.

  • Crontab access: lynis_hardening_cron_allow: true ensures /etc/cron.allow exists with root:root ownership and mode 0640 on the supported platforms, preserving existing entries and timestamps. A new empty file permits only root to use crontab; manage authorized users through your account policy. This does not remove existing jobs, install cron, or change /etc/cron.deny. Select the task with file_cron_allow_exists.

  • Scheduled job access: lynis_hardening_at_allow: true ensures /etc/at.allow exists with root:root ownership and mode 0640 on the supported platforms. Existing entries and timestamps are preserved. A newly created empty file restricts at access to root; add authorized users through your account policy if required. This does not install at or change /etc/at.deny. Select the task with file_at_allow_exists.

  • Local log permissions: lynis_hardening_log_file_permissions: true recursively restricts regular files under /var/log to 0640 or less permissive using u-xs,g-xws,o-xwrt. Existing stricter permissions and ownership are preserved; directories and symlinks are not modified. lynis_hardening_log_file_exclusions defaults to history.log*, eipp.log.xz*, [bw]tmp, [bw]tmp.*, [bw]tmp-*, lastlog, and lastlog.*, matching basenames at every depth. The rule also inspects hidden files and fails if paths cannot be searched. Select it with permissions_local_var_log; it runs independently of remote logging and supports check mode. Configure matching creation modes in application/logrotate policies to keep newly created logs compliant between role runs.

  • User initialization files: lynis_hardening_user_init_files: true restricts matching regular dotfiles directly inside eligible home directories to mode 0740 or less permissive, using u-s,g-wxs,o= without adding permissions. Defaults select UIDs >= 1000 and the filename regex ^\.[\w\- ]+$; this matches dotfiles beyond shell startup files, such as .gitconfig. Symlinked homes/files, / as a home, nobody/nfsnobody, and accounts using false/nologin shells are skipped. Configure lynis_hardening_user_init_files_regex, lynis_hardening_user_init_uid_min, lynis_hardening_user_init_excluded_users, and lynis_hardening_user_init_excluded_shells to match local account policy. The rule does not recurse, change ownership, or create missing homes. Select it with the file_permission_user_init_files tag.

  • Empty-password authentication: lynis_hardening_no_empty_passwords: true removes the nullok option from active Unix PAM entries. Debian receives a managed lynis_unix profile derived from the installed unix profile, with Conflicts: unix; the packaged profile remains unchanged. Activation uses pam-auth-update without --force, followed by cleanup of the five common PAM stacks. Rocky enables without-nullok on the existing valid authselect profile with a backup; it does not replace the profile. Unsupported or locally modified authselect configurations fail rather than being forced. This addresses no_empty_passwords_unix. Test a new login and sudo session while retaining console access. Account passwords and account lock states are not changed. Check mode reports Debian file changes but skips profile activation and final verification; Rocky feature activation is also skipped in check mode. The intended default configuration covers:

  • SSH: PermitEmptyPasswords=no, AllowTcpForwarding=no, AllowAgentForwarding=no, ClientAliveInterval=300, ClientAliveCountMax=2, LogLevel=VERBOSE, MaxSessions=2, and TCPKeepAlive=no.

  • /etc/sudoers.d: owner/group root:root and mode 0750, without recursively changing its contents.

  • Sudo command logging: lynis_hardening_sudo_logging: true configures Defaults logfile="/var/log/sudo.log" in a managed block in /etc/sudoers, validated with visudo -cf before saving. The logfile is created with root:root ownership and mode 0600, preserving existing contents and timestamps. Set lynis_hardening_sudo_logfile to another filename directly under /var/log if needed. Manage rotation through the host's log rotation policy. This addresses sudo_custom_logfile; verify a new sudo command and rerun the compliance scan after deployment. Scoped sudoers Defaults may override the global setting.

  • PAM password quality: at least 14 characters, three character classes, difok=4, retry=3, and enforce_for_root.

  • New accounts: PASS_MIN_DAYS=1, PASS_MAX_DAYS=365, and PASS_WARN_AGE=14.

  • Core dumps: lynis_hardening_core_dumps: true manages zero soft/hard core limits for wildcard users and root in /etc/security/limits.d/90-lynis-hardening.conf and disables systemd-coredump processing/storage. Conflicting nonzero * hard core rules in /etc/security/limits.conf and regular local limits.d/*.conf files are commented out with backups, including files that also contain a correct zero rule. Other resource limits and existing correct zero rules are retained. Select the tasks with disable_users_coredumps. Existing process limits change only after a new session. Verify the active PAM stack uses pam_limits.so and review named user/group exceptions, vendor drop-ins, and alternative conf= paths separately; these are not rewritten. This supplements fs.suid_dumpable, which already meets the report's requirements.

SSH forwarding and agent forwarding are disabled by default; adjust these settings if your administration relies on tunnels, bastions, or agent forwarding. The SSH port, root login, authentication methods, and user lists are left unchanged. MaxSessions=2 limits sessions per connection, without limiting separate connections.

On Debian, the intended PAM configuration activates the packaged pwquality profile through pam-auth-update. Rocky requires an existing active pam_pwquality entry in both system-auth and password-auth; otherwise, the role should stop with an explanation. Existing authselect profiles and SSSD configuration are retained. Check existing PAM module options, which may override pwquality.conf.

Optional Features

Fail2ban is optional because it is already present in the report. On Rocky, make EPEL available beforehand if required. The configuration uses a managed jail.d/*.local override. Set lynis_hardening_fail2ban_jail_local: true to additionally copy jail.conf to jail.local once, preserving any existing jail.local. This copy is skipped in check mode. Review the copied settings when upgrading Fail2ban. Check existing jails with matching sections for conflicts.

Remote logging uses TCP/TLS with CA and server name verification. The collector and CA must be available beforehand. The proposed default CA path is suitable for Rocky; configure the appropriate path for Debian. The queue is held in memory and does not survive a crash. Validate configuration before restarting rsyslog. If validation fails, the handler should stop while leaving the changed file on disk for recovery. Review existing global rsyslog TLS settings alongside this configuration.

Service drop-ins take effect after a daemon reload and the next restart of the affected service. Schedule those restarts yourself. Removing dictionary entries does not remove existing drop-ins; remove them explicitly.

AIDE configuration should neither modify an existing database nor initialize a trusted baseline automatically. Check the effective distribution configuration with aide --config-check; Debian may use configuration generated through aide.wrapper. Review includes and selection rules, as more specific rules can take precedence. Then deliberately initialize or refresh the trusted database before the first scheduled check. Otherwise, changes may be reported incorrectly or go undetected. Validate this optional feature on each distribution.

Periodic AIDE Checks

When AIDE and lynis_hardening_aide_timer are enabled, the role installs aidecheck.service and enables and starts aidecheck.timer. The default schedule is daily at 05:00 in the target host's timezone. Persistent=true catches up missed runs. The service is Type=oneshot and is triggered by the timer; it is not enabled separately at boot. Calendar expressions are validated with systemd-analyze calendar before deploying units. Changes to the schedule restart the timer, while unchanged runs leave it running.

lynis_hardening_aide_check_command defaults to /usr/sbin/aide --config=/etc/aide/aide.conf --check on Debian and /usr/sbin/aide --config=/etc/aide.conf --check on Rocky, matching the managed SHA512 configuration. Override it if your host uses a wrapper or a generated configuration. Initialize the trusted database manually and review existing distro timers or cron jobs to avoid duplicate scans. A missing database causes checks to fail; the role does not silently initialize or update it. Review results with systemctl status aidecheck.timer and journalctl -u aidecheck.service.

The supplied compliance tags, including aide_periodic_checking_systemd_timer, select timer configuration. Install AIDE and configure its policy first with a full role run. Check mode renders the units without enabling or starting the timer. Setting the timer variable to false stops managing it; stop and disable an already installed timer explicitly when retiring it.

Startup, Service, and Firewall Assessment

Set lynis_hardening_assessment: true to print the systemd default target, enabled and running services, a systemd-analyze security overview, and IPv4/IPv6 firewall rules with counters. Native nftables rules are also reported, with counters where configured. Use lynis_hardening_assessment_services for detailed security reports on selected service units. These read-only commands also run in check mode and can be selected with the assessment tag. Review each command's return code and error output: unavailable tools are reported without aborting the other assessments.

The reports support manual review; they do not automatically disable services, apply generic sandbox settings, or remove firewall rules. Compare firewall counters over representative traffic before deciding a rule is unused. Configure tested service changes through lynis_hardening_disable_services and lynis_hardening_service_overrides. Drop-ins still require a service restart before taking effect; an assessment before that restart does not prove runtime restrictions are active.

The playbook enables these reports, AIDE, Fail2ban, and selected compiler restrictions. It does not choose a replacement SSH port or speculative sysctl values. For KRNL-6000, provide the specific keys and expected values from the scan in lynis_hardening_sysctl. AIDE still requires effective-rule validation and deliberate baseline initialization as described above.

Lynis Recommendations

The following table maps the supplied report to the intended implementation and manual follow-up:

Check Handling
LYNIS Update Lynis through a trusted package source. No external repositories or general system upgrades are planned.
DEB-0880 Optional managed Fail2ban .local override and one-time creation of jail.local without overwriting an existing file.
BOOT-5122 Configure GRUB password protection manually using a PBKDF2 hash managed through encrypted storage. Check BIOS/UEFI settings and test console recovery. No generic implementation is planned.
BOOT-5180 Optional report of the default target and enabled/running services. Stop and disable services only through an explicitly selected list.
BOOT-5264 Optional security overview and detailed reports for selected services, plus configurable service drop-ins. Review systemd-analyze security SERVICE and test functionality. Generic restrictions on auditd, SSH, dbus, or getty can break administration.
AUTH-9230 Preserve existing password hashing policy. YESCRYPT_COST_FACTOR and SHA_CRYPT_* in login.defs do not independently control users' PAM hashes. Configure any work factor in the actual PAM/authselect policy, benchmark it, and rotate passwords. Do not downgrade yescrypt to SHA512 to improve a score.
AUTH-9262 PAM pwquality, with verification of the active PAM stack.
AUTH-9282 Optional absolute expiry date for explicitly selected existing accounts. Account expiry also blocks SSH key access.
AUTH-9286 login.defs settings for new accounts and explicit user settings for existing accounts.
FIRE-4513 Optional report of IPv4/IPv6 rules and counters, plus native nftables rules. Manually review counters over representative traffic. A rule without hits is not automatically redundant. No automatic rule removal is planned.
SSH-7408 Eight managed settings. Keep port 22; changing the port does not replace access control and requires firewall/SELinux coordination. Configure AllowUsers/AllowGroups only through explicit custom options.
LOGG-2154 Optional authenticated TLS forwarding to your collector.
FINT-4402 Optional SHA512 AIDE rule for explicit paths. Check the effective policy, then refresh the trusted baseline manually.
TOOL-5002 Controller-driven Ansible management requires no Ansible agent on the target host. Lynis may continue to report this suggestion.
KRNL-6000 Explicit sysctl dictionary. Only kernel.modules_disabled differs in the supplied report. Do not change it by default: setting it to 1 blocks loading and unloading modules until reboot, including modules needed for storage, networking, and updates.
HRDN-7222 Set lynis_hardening_compiler_paths to explicitly selected compiler binaries. Existing regular files, including symlink targets, receive root:root ownership and mode 0700. Include version-specific compiler paths where needed. Package upgrades may reset permissions; reapply the role afterward. This can affect build workloads and is not a strong security boundary. Remove unused compiler packages through separate package policy.

Dependencies

No role dependencies are currently declared in meta/main.yml.

The proposed implementation may require Ansible collections; a collection requirements file has not yet been added. Optional features also require their corresponding host packages and services.

Example Playbook

Create site.yml in your playbook project after installing the role:

---
- name: Harden Linux systems
  hosts: all
  become: true
  gather_facts: true
  serial: 1

  roles:
    - role: ansible-role-hardening
      vars:
        lynis_hardening_fail2ban: true
        lynis_hardening_remote_logging: false

Testing

The repository includes a Molecule test scaffold. The hardening implementation, YAML, and playbook syntax still need validation. Installation, PAM behavior, SSH connectivity, TLS forwarding, AIDE rule selection, and idempotence have not been verified on all three target operating systems.

Running Tests Locally

Prerequisites

Install the Python dependencies:

python -m pip install --upgrade -r requirements.txt

Ensure the Docker daemon is running:

sudo systemctl start docker

Running Molecule Tests

Run the configured test scenario:

molecule test

Keep containers available for debugging:

molecule converge
molecule verify

Review and update the Molecule platform configuration before using it to validate Debian 13 and Rocky Linux 9/10. The existing scaffold does not establish support for the proposed hardening behavior.

Linting

ansible-lint
yamllint .

Functional Validation

Run the role twice on each test VM. The second run should report no changes. Then verify a new SSH login, sudo, a password change for a test account, application functionality, and, if enabled, receipt of logger messages on the collector.

Compare a new lynis audit system report against the original. Some suggestions may remain; the role does not promise a score of 100.

Troubleshooting

Common Issues

SSH configuration or connectivity:

  • Validate the complete candidate configuration using sshd -t -f before saving it. The intended managed block belongs at the top because OpenSSH uses the first value found.
  • Existing Match blocks may change behavior for individual connections. Check sshd -T -C user=ansible,host=server,addr=ADMIN_IP.
  • A syntax check does not prove connectivity. Keep console access and a second SSH session available. The implementation should retain backups of modified main configuration files on the target host.

Check mode fails on a fresh host:

  • Configuration files may be missing when check mode skips package installation. The proposed implementation skips PAM activation and verification in that situation.

Molecule container will not start:

  • Ensure Docker is running and your user has permission to access it.
  • Recreate the test containers with molecule destroy followed by molecule test.

Ansible connection errors:

  • Verify SSH keys, firewall rules, and the inventory's ansible_user setting.

For more help, open an issue or contact support.ds.nl@spie.com.

References: OpenSSH configuration, Debian 13 login.defs, authselect PAM profiles.

License

MIT - See LICENSE file for details.

Copyright (c) 2026 SPIE DataSolutions B.V.

Changelog

See CHANGELOG.md for a list of all notable changes to this project.

Security

Please see our Security Policy for reporting security vulnerabilities.

Contributing

Please read our Contributing Guide and Code of Conduct before submitting a Pull Request.

Support

For questions or support, contact: support.ds.nl@spie.com