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-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-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
Live-tested

ansible-bandit

bandit on EL 10 from PyPI into a venv of its own with a link in the PATH; the live test runs pip check, then scans a module that passes a string to subprocess.call with shell=True (B602, High, exit 1) and a clean module (exit 0). Pinned. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-boundary

boundary on EL 10 from releases.hashicorp.com; SHA256SUMS is signature-checked against HashiCorp's key before get_url trusts it, the live test re-checks both, then runs authenticate against a dead address ('connection refused') and database init with a throwaway config (parsed, root key loaded, then the database refused). Pinned. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-dockle

dockle on EL 10 from the GitHub release; get_url refuses it unless its SHA-256 is in the vendor's checksums file, and the live test re-checks it, then scans a one-layer image whose config names no user, expecting CIS-DI-0001 in the output, exit code 1 with --exit-level warn, and the warning counted in the JSON form. Pinned. Original role, live-tested on Rocky Linux 10.

View module
Live-tested

ansible-gitleaks

gitleaks on EL 10 from the GitHub release, refused by Ansible's get_url unless its SHA-256 is the one in the project's checksums file, and re-checked with sha256sum -c by the live test, which then plants a file holding an AWS access key that is not one and runs gitleaks detect over it, expecting 'leaks found: 1'. Original role, live-tested on Rocky Linux 10.

View module