OpenSSH Hardened Where sshd Reads It First
An sshd drop-in numbered 01, so it is read before the 50-redhat.conf that asks for X11 forwarding: root login off, passwords off, MaxAuthTries 4, idle timeouts. The live test proves the policy with the daemon rather than the file: a password login refused, a key login accepted, root refused, and the banner delivered before authentication. Original role, live-tested on Rocky Linux 10.
Verification
Live-testedReally deployed to a container sandbox, proven idempotent (a second run changes nothing), verified against the role’s assertions, then torn down.
Conformance
- Static validation (yamllint · ansible-lint)
Provenance
- SHA-256 checksum
- Signature (pending)
Functional
- Live-tested - applied, verified, destroyed
Last verified 2026-09-26 · podman 4.9.3 · ansible 2.21.4 · how we verify
Cite it in your README
badge · attributionPaste this beside the module in the repository that uses it. The badge is rendered from this artifact's verification record, so it reads live-tested because the record says so, and the link lands on this page.
[](https://www.iac-bazaar.com/catalog/ansible-ssh-hardening?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)
Ansible role 1.0.0, live-tested on IaC Bazaar: [OpenSSH Hardened Where sshd Reads It First](https://www.iac-bazaar.com/catalog/ansible-ssh-hardening?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)
```yaml
# OpenSSH Hardened Where sshd Reads It First: https://www.iac-bazaar.com/catalog/ansible-ssh-hardening (download from your IaC Bazaar account)
```Preview:
Documentation
ssh-hardening
An SSH policy that takes effect, proven by a refused login. The role writes
one drop-in numbered to be read before the distribution's own, checks the
configuration with sshd -t before anything is reloaded, and enables the unit EL
ships. The live test reads sshd -T for the effective policy and then attempts
three real logins: a password, a key, and root.
No download, and no version to pin. EL 10 packages OpenSSH, so the role installs it by name and manages the unit the distribution ships. What the role owns is the policy: who may log in, with what, for how long, and what is written down about it.
The number in the filename is the whole trick. sshd takes the FIRST
occurrence of a directive it reads, and EL ships
/etc/ssh/sshd_config.d/50-redhat.conf with X11Forwarding yes. A hardening
drop-in numbered above 50 is read second and silently loses; this one is
01-iacbazaar-ssh.conf. The live test asserts both halves together: that
forwarding is off in sshd -T, AND that the 50 file still says yes, so a future
rename could not pass quietly.
The algorithms are deliberately not here. EL's
40-redhat-crypto-policies.conf includes the system crypto policy's own
opensshserver.config, and sshd -T shows its ciphers, MACs and key exchange
list (post-quantum mlkem768x25519-sha256 among them on EL 10). Because this
role's file is read FIRST, a Ciphers line in it would override the entire
system policy from one drop-in. It sets none, and the live test asserts the
policy include is still in place. Change the algorithms with the crypto-policy
role, where the change applies to every service at once.
PasswordAuthentication no is not enough on its own. With PAM in the stack,
a password can still arrive through keyboard-interactive, so the role sets
KbdInteractiveAuthentication to match and the live test proves it by trying a
password login and reading the refusal.
A banner file is not a banner. Banner is what makes sshd read
/etc/issue.net; nothing else on EL reads that file. The live test asserts the
text arrives at a client that has not authenticated.
License
Commercial - IaC Bazaar EULA. (c) IaC Bazaar.
Usage code & full reference need an account
The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.
- Variables
- Test
Related modules
ansible-lynis
Lynis from EPEL on a systemd timer, with the units the package keeps only in its docs, your skipped tests in custom.prf, and a minimum hardening index below which the audit unit fails, so a falling score shows as a failed unit. The live test runs the audit, reads the index and the tests that ran, and finds the timer enabled. Original role, live-tested on Rocky Linux 10.
ansible-suricata
Suricata from EPEL watching the default-route interface as the suricata user, ET Open fetched at install and refreshed daily, plus a canary rule that proves the sensor is alive. A root-run check left root-owned logs and a daemon recording nothing; the role prevents it. The live test raises the canary alert while a second lookup is logged, not alerted. Original role, live-tested on Rocky Linux 10.
ansible-aide
AIDE from AppStream on EL 10: a watched tree, a baseline database built once, and the oneshot unit and timer the package does not ship. The live test passes on an unchanged host, FAILS when a file appears inside a watched path and names it, ignores one inside an excluded path, and passes again once the change is gone. Original role, live-tested on Rocky Linux 10.
ansible-389-ds
389 Directory Server from AppStream. A fresh instance listens on every interface and lets anonymous clients read the tree; this role binds both listeners to loopback and limits anonymous clients to the rootDSE. The live test adds a user who binds, sees a wrong password and an anonymous search refused, and reads the LDAPS certificate. Original role, live-tested on Rocky Linux 10.
ansible-krb5-kdc
An MIT Kerberos KDC: the realm created once with a stash, kadmind behind an ACL, a default policy that locks an account after five wrong passwords, every listener on loopback. The KDC starts on a broken config without a word, so the live test proves each setting by behaviour: the ticket lifetime, the refusals, the lockout. Original role, live-tested on Rocky Linux 10.
ansible-authelia
Authelia from the upstream release (sha256-verified) as a hardened system service on loopback with file users and sqlite; its secrets are generated once on the host and reach the service as AUTHELIA_*_FILE variables. The live test hashes a password with Authelia's hasher, logs in, sees a wrong password refused and an anonymous visitor sent to the portal. Original role, live-tested on Rocky 10.