The System Crypto Policy, Proven To Govern The Handshake

One EL 10 setting that decides what OpenSSL, GnuTLS, NSS, OpenSSH and Java will negotiate, applied only when it differs and read back from both files that record it. The live test runs one TLS 1.2 handshake three times, under the configured policy, under FUTURE, and under the policy again, so the refusal in the middle is the policy's doing. Original role, live-tested on Rocky Linux 10.

ansibleSecurity & Secrets

Verification

Live-tested

Really 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-27 · podman 4.9.3 · ansible 2.21.4 · how we verify

Cite it in your README

badge · attribution

Paste 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.

README.md, GitLab, Gitea
[![IaC Bazaar: live-tested](https://www.iac-bazaar.com/api/artifacts/ansible-crypto-policy/badge)](https://www.iac-bazaar.com/catalog/ansible-crypto-policy?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

Ansible role 1.0.0, live-tested on IaC Bazaar: [The System Crypto Policy, Proven To Govern The Handshake](https://www.iac-bazaar.com/catalog/ansible-crypto-policy?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

```yaml
# The System Crypto Policy, Proven To Govern The Handshake: https://www.iac-bazaar.com/catalog/ansible-crypto-policy (download from your IaC Bazaar account)
```

Preview:IaC Bazaar: live-tested

Documentation

crypto-policy

One switch that every library on the host obeys. EL's crypto policies decide what OpenSSL, GnuTLS, NSS, OpenSSH, Java and Kerberos will negotiate, from one setting. The role sets it, refuses to believe it without reading it back, and proves the new policy reached a running daemon rather than only a file. The live test runs one handshake three times - under the configured policy, under FUTURE, and under the configured policy again - so the refusal in the middle is demonstrably the policy's doing and not the server's.

No download, and no version to pin. The crypto policies and their scripts are part of EL 10. What the role owns is which policy the host runs, and the proof that the host is really running it.

There is no file for this role to write. The policy is a system-wide switch, and setting it regenerates every back-end under /etc/crypto-policies/back-ends. So the role reads the current policy, sets it only when it differs, and reads it back - and the live test asserts the generated openssl back-end really carries the floor the policy implies.

The live test starts this host on the wrong policy on purpose. A role whose whole effect is one conditional task converges with changed=0 against a machine that already matches it, and a verify run after that reads the distribution's defaults while appearing to confirm the role. The lane refuses a first converge that changed nothing, and tests/pre-converge.yml answers the refusal by putting the host on LEGACY first. What the receipt certifies is therefore a policy this role moved, not a policy it agreed with.

A running process keeps the policy it read at start, and EL restarts exactly three of them. update-crypto-policies --set runs /usr/share/crypto-policies/reload-cmds.sh, which issues try-reload-or-restart for bind and try-restart for ipsec and sshd, skipping any of the three that is not already running. Nothing else is touched: httpd, postfix, dovecot, a JVM, a database and anything of your own go on negotiating under the policy they read when they started, while --show reports the new one. That is the gap crypto_policy_restart_services exists to close, and it is empty by default because sshd - the one everybody thinks of - is already covered.

Measured, because the tool's own note ("it is recommended to restart the system") suggests it does nothing of the kind: a plain --set moved sshd's activation forward by 9 seconds, --no-reload --set moved it by 0, and a plain --set again by 6. crypto_policy_reload_services: false is that flag, for a host where sshd dropping its sessions is your decision to schedule rather than a side effect of a policy change.

The live test asserts the new policy reached a running daemon: sshd is installed and started before the converge, and its activation must be no earlier than the moment the policy file was written. A name in the restart list that systemd does not know fails the run - it used to be swallowed, which meant the role could report success having reached no running process at all.

Two of the obvious hardening choices stop dnf working on Rocky 10. Both FUTURE and DEFAULT:OSPP raise OpenSSL's security level to 3, and the repository mirrors present RSA-2048 certificates that a level-3 client refuses, so package installs fail with Curl error (60) ... EE certificate key too weak. This role's own live test failed on exactly that before the default was changed. Measured on Rocky Linux 10 in this role's lane, one policy per row:

policydnf makecachea TLS 1.2 peer
DEFAULTworksaccepted
DEFAULT:NO-PQworksaccepted
DEFAULT:ECDHE-ONLYworksrefused, handshake failure
DEFAULT:OSPPfailsaccepted
FUTUREfailsrefused, protocol version

The dnf column is a fact about the certificates this host's repositories serve, not about the policy alone: a mirror with a 3072-bit key would be reachable under either. Check your own repositories, internal ones included, before choosing a level-3 policy - and note that --set succeeds in every row above, so nothing warns you at the moment you make the change.

DEFAULT:NO-SHA1 does not work on EL 10. It is the line every RHEL 9 hardening guide gives, and NO-SHA1.pmod is not shipped here: the command exits non-zero with Unknown policy, and the previous policy stays in force. The subpolicies Rocky 10 does ship are AD-SUPPORT, AD-SUPPORT-LEGACY, ECDHE-ONLY, NO-ENFORCE-EMS, NO-PQ, OSPP and TEST-PQ. The live test asserts that refusal, so if a later release adds the module this role goes red rather than going stale.

Setting the FIPS policy is not FIPS mode. update-crypto-policies --set FIPS selects the algorithm set; a host in FIPS mode needs fips-mode-setup --enable and a reboot, which this role does not do and does not claim.

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

Live-tested

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.

View module
Live-tested

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.

View module
Live-tested

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.

View module
Live-tested

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.

View module
Live-tested

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.

View module
Live-tested

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.

View module