access.conf, In The Stack And Proven With PAM Itself

EL ships /etc/security/access.conf with no active rule and pam_access is not in the stack to read it, so every account is admitted from anywhere. This role writes the policy, adds the module through authselect, and refuses to write a rule set that would lock out the account running it. The live test asks PAM, with the origin set. 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-pam-access/badge)](https://www.iac-bazaar.com/catalog/ansible-pam-access?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

Ansible role 1.0.0, live-tested on IaC Bazaar: [access.conf, In The Stack And Proven With PAM Itself](https://www.iac-bazaar.com/catalog/ansible-pam-access?utm_source=syndication&utm_medium=readme&utm_campaign=artifact)

```yaml
# access.conf, In The Stack And Proven With PAM Itself: https://www.iac-bazaar.com/catalog/ansible-pam-access (download from your IaC Bazaar account)
```

Preview:IaC Bazaar: live-tested

Documentation

pam-access

EL ships /etc/security/access.conf with no rules in it, and pam_access is not in the stack to read them. 4,671 bytes of documentation, a control that appears configured, and every account admitted from anywhere. This role writes the policy, puts the module into the authentication stack through authselect - /etc/pam.d/system-auth is a symlink into generated files, so editing it by hand does not survive - and proves each rule by asking PAM.

No download, and no version to pin. pam and authselect are EL 10 packages. What the role owns is access.conf in full and one authselect feature, and what it proves is that a named account gets in from a named place and nobody else does.

The order of the file is the policy. pam_access stops at the first rule that matches, so a blanket allow above a deny makes the deny dead. That is why the template renders the rules in a fixed order and why pam_access_prepend_rules is documented as the dangerous escape hatch rather than the convenient one.

This role can lock you out of a host, so it checks first. With pam_access_deny_all and a group list that does not include you, the next login is refused - and after - : ALL : ALL, root's ssh logins are refused too, because pam_access_allow_root_from is LOCAL by default. Before writing anything the role reads the account this play is connected as, the groups the host gives it, and whether the connection arrived over ssh, and refuses to write a policy that would shut that account out. The check has one limit, stated because it matters: it requires that a non-LOCAL origin exists for root, not that the origin is yours.

A line pam_access cannot parse is ignored, and the account is admitted. Measured: with this is not a rule in the file, the account under test was still allowed. Nothing validates access.conf - there is no checker to give a template validate: - so the live test drives PAM with pamtester rather than reading the file back, and it reads the refusal message as well as the exit code.

First match wins, in both directions. With + : ALL : ALL above - : user : ALL the user was admitted; with the deny first the same user was refused. This is the trap that makes a long access.conf unreadable: a rule added at the top for one host silently retires everything below it.

LOCAL is a real origin, not the absence of one. A rule allowing a user from 10.99.0.0/24 refused that user from 203.0.113.7 and from a console login with no remote host at all. + : root : LOCAL is what admits root on the console, and it is what makes root's network logins refused.

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