Dotfiles/docs/md/ansipa-administration.md

18 KiB

ansipa Administration Guide

Day-to-day operation of the ansipa management plane: the full policy catalogue and how to apply, target, and revert each policy. For the deployment/ports/ reverse-proxy story see the ansipa Setup Guide; for the architecture and container internals see FreeIPA & Ansible.


How administration works

Everything is driven by FreeIPA group membership. You never log into a client to change its policy — you add or remove a host or user from a group on the FreeIPA server, and the fleet converges on the next timer tick.

Two independent client-side mechanisms consume that membership, both installed by the Ansible playbooks and both idempotent and reversible (leaving a group undoes the change on the next run):

Mechanism Timer Reads Applies
Policy enforcer (ansipa-enforce-policies.sh) ansipa-enforce-policies.timer, every 30 min host + user groups daemon/sudo/ssh/monitoring/scan/SMB/block policies
Installer timers (ansipa-install-*.sh) ansipa-install*.timer, every 30 min dev_pkg_*, dev_mod_*, dev_fp_* host groups packages, setup modules, Flatpaks

Because it is all client-pull, everything works across NAT (the server never initiates a connection to a client). To apply a change immediately instead of waiting for the timer, run the relevant script on the client as root:

sudo /usr/local/bin/ansipa-enforce-policies.sh     # policies
sudo /usr/local/bin/ansipa-install-packages.sh     # dev_pkg_*
sudo /usr/local/bin/ansipa-install-modules.sh      # dev_mod_*
sudo /usr/local/bin/ansipa-install-flatpaks.sh     # dev_fp_*

The two group scopes

  • Host groups (dev_*) — a device policy. Applies to the machine(s) you add to the group. Managed with ipa hostgroup-*.
  • User groups (usr_*) — a user policy that follows the user to every enrolled machine they log into. Applies while the group exists; managed with ipa group-*. Several user policies key off the group merely existing (not the specific host), because the enforcer runs on every host.

The two ways to bind a target

Most policies encode their target in the group name suffix (dev_local-sudo-alice, dev_pkg_htop, usr_block-binary-nano). A few need extra data (a printer URI, an SMB credential) which lives in the group description, set with ipa group-mod <grp> --desc='…' — noted per-policy below.

Everyday commands

# Create + populate a host group (device policy)
ipa hostgroup-add dev_mon_base --desc "CheckMK monitored hosts"
ipa hostgroup-add-member dev_mon_base --hosts=client01.corp.example.com
ipa hostgroup-remove-member dev_mon_base --hosts=client01.corp.example.com

# Create + populate a user group (user policy)
ipa group-add usr_admin --desc "Fleet-wide sudoers"
ipa group-add-member usr_admin --users=alice
ipa group-remove-member usr_admin --users=alice

# Inspect
ipa host-show client01.corp.example.com | grep -A3 'Member of host-groups'
ipa hostgroup-show dev_pkg_htop

Everything below can equally be done from the FreeIPA web UI (Identity → Host Groups / User Groups).


Device policies (dev_* host groups)

dev_daemon-enable-<unit> / dev_daemon-disable-<unit>

Force a systemd unit on or off fleet-wide.

  • Apply: enable → systemctl enable --now <unit>; disable → disable --now.
  • Revert (leave group): the opposite action.
  • The .service suffix is optional (appended automatically for a bare name); any unit type works. A unit in both enable and disable groups is skipped with a warning.
  • Example: ipa hostgroup-add dev_daemon-disable-bluetooth then add hosts → bluetooth.service is stopped and masked-off on them.

⚠️ Enabling sshd this way and later reverting will stop sshd — don't lock yourself out of a headless box you only reach over SSH.

dev_timeshift-backup

Enforce a daily Timeshift snapshot at 03:00 (requires timeshift; add the host to dev_mod_timeshift to install it). Leaving the group removes the cron.

dev_no-local-users

Lock the password of every local account — root (UID 0) and all regular users (UID ≥ 1000) — with passwd -l, so only FreeIPA accounts can authenticate. Accounts already locked are left untouched and not tracked; revert unlocks only what this policy locked. State: /var/lib/ansipa-policies/no-local-users.

dev_local-sudo-<username>

Grant <username> full sudo on this specific device via a sudoers drop-in. Device-scoped (unlike usr_admin). Leaving the group removes the drop-in.

  • Example: ipa hostgroup-add dev_local-sudo-alice + add lab-pc-3 → alice gets sudo on lab-pc-3 only.

dev_ssh_<userid>

Distribute <userid>'s SSH public keys from their IPA profile to /home/<userid>/.ssh/authorized_keys on hosts in the group (in an ansipa-managed section; hand-added keys are preserved). Store the keys in IPA:

ipa user-mod alice --sshpubkey='ssh-ed25519 AAAA...'
ipa hostgroup-add dev_ssh_alice && ipa hostgroup-add-member dev_ssh_alice --hosts=

Leaving the group removes only the ansipa-managed section.


Monitoring policies (dev_mon_*, usr_mon_*)

All CheckMK monitoring requires the CheckMK stack to be up and ansipa-checkmk-setup.sh to have run on the FreeIPA container (seeds the dev_mon_base credentials + the monitoring groups in IPA). All checks are collected by the agent, so every monitored host must also be in dev_mon_base.

dev_mon_base (required for all monitoring)

Installs the CheckMK agent, registers the host in CheckMK (under /ansipa), and opens the agent for collection: pull mode on check_mk.socket (port 6556) where the server can reach the client, or push mode (cmk-agent-ctl → server :8000) for NAT clients. Built-in CPU/RAM/disk/uptime plus an installed-package count check (Ansipa_Packages). Leaving the group deregisters the host and disables the agent.

Credentials are auto-discovered from the dev_mon_base group description (cmk://<host>:<port>/<site>:automation:<secret>), which the server populates. Set CMK_ADVERTISED_URL in .env to a client-routable address.

dev_mon_clamscan

Run a daily ClamAV + rkhunter + chkrootkit scan at 02:00 and report the result to CheckMK as the Ansipa_ClamScan service (OK = clean, WARN = stale DB or rkhunter warnings, CRIT = infected). Self-contained — it runs the scan and writes the local check; results no longer go over SMB.

  • Prerequisites: dev_mod_anti-malware (tools) + dev_mon_base (agent).
  • Revert: removes the cron, scan script, log, and local check.

dev_mon_timeshift

Ansipa_Timeshift check on the newest Timeshift snapshot age: WARN > 5 days, CRIT > 10 days or none found. Pair with dev_timeshift-backup (which creates them) and dev_mon_base.

dev_mon_power

Ansipa_Power check on CPU-package power via Intel RAPL (preferred) or lm-sensors: WARN ≥ 65 W, CRIT ≥ 95 W. Add the host to dev_mod_* that installs lm_sensors if RAPL is unavailable.

usr_mon_logins (user group)

Ansipa_Logins check reporting failed local login attempts in the last 24 h — console/tty, graphical greeters (gdm/sddm/greetd/lightdm): anything authenticating via PAM that isn't sshd or sudo. Reports failed + invalid-user counts, with thresholds. Installed fleet-wide when the group exists; needs dev_mon_base on the hosts. SSH activity is tracked separately by usr_mon_sshlogins; sudo/access-control denials by usr_mon_rightsviolations — both below.

usr_mon_sshlogins (user group)

Ansipa_SSHLogins check reporting SSH login attempts in the last 24 h (successful + failed + invalid-user), with thresholds. Installed fleet-wide when the group exists; needs dev_mon_base on the hosts.

usr_mon_rightsviolations (user group)

Ansipa_RightsViolations check reporting authorization/access-control denials by an already-authenticated user in the last 24 h — not "who are you" failures (that's the two checks above), but "you are who you say, but that's not allowed": sudo denials (user NOT in sudoers, command not allowed, incorrect sudo password — works on every distro, sudo always logs these), plus SELinux AVC denials (Fedora/RHEL, only when getenforce reports Enforcing) and AppArmor denials (Debian/Ubuntu, only if loaded). Each source degrades gracefully when not applicable to the host — this is a best-effort union of what the system already logs, not a guarantee of catching every possible file access denial; watching arbitrary paths would need explicit auditd watch rules on specific files, which is a deliberate per-host decision this check does not make for you. WARN ≥ 3 total, CRIT ≥ 10 total. Installed fleet-wide when the group exists; needs dev_mon_base on the hosts.

usr_mon_iploc (user group)

Ansipa_IPLoc check reporting this device's current public IP and its geolocation (city/region/country/ISP), and WARNing when the observed country changes since the last reading — e.g. a device that's normally in one country suddenly appearing in another.

  • Uses two free, no-API-key third-party services: api.ipify.org (what's my public IP) and ip-api.com (IP → geolocation). The device's public IP is sent to both on every actual lookup — be aware of that before enabling this fleet-wide.
  • Reports the network egress location, not necessarily the device's physical location: a host behind a VPN will show the VPN exit country, and that will (correctly, if noisily) trigger a "country changed" WARN the moment the VPN connects or disconnects.
  • No hardcoded country blocklist — any change from the last-observed country trips a WARN once, then returns to OK on the next reading if the country is stable at the new location. This is intentionally an anomaly ("it just moved") signal, not a permanent flag on any particular country.
  • CheckMK invokes local checks on its own polling cadence (often every minute) — far more often than an external API should be hit. This check self-throttles to one real lookup per 30 minutes and replays its last result in between; the geolocation call additionally only re-fires when the public IP itself has changed.
  • Installed fleet-wide when the group exists; needs dev_mon_base on the hosts.

Software-installer policies (dev_pkg_*, dev_mod_*, dev_fp_*)

These are the GPO-like software-deployment groups, applied by the ansipa-install-* timers (not the policy enforcer). Join → installed; leave → removed. Install state is tracked so revert only touches what ansipa installed.

dev_pkg_<package>

Install a single distro package (via pacman/dnf/apt — auto-detected).

ipa hostgroup-add dev_pkg_htop && ipa hostgroup-add-member dev_pkg_htop --hosts=

dev_mod_<name>

Apply a whole setup module from setup/modules/optional-Modules/apps/ — e.g. dev_mod_docker, dev_mod_anti-malware, dev_mod_timeshift, dev_mod_ollama. This is how you install the tools other policies need (dev_mod_anti-malware before dev_mon_clamscan, dev_mod_timeshift before dev_timeshift-backup).

dev_fp_<app>

Install a Flathub Flatpak. Encode the app ID with __ in place of .:

# org.gimp.Gimp
ipa hostgroup-add dev_fp_org__gimp__Gimp
ipa hostgroup-add-member dev_fp_org__gimp__Gimp --hosts=

User policies (usr_* user groups)

usr_admin

Full sudo on every enrolled host for members, via a %usr_admin sudoers drop-in resolved through SSSD. Active while the group exists; the drop-in is removed when the group is deleted (not merely when a member leaves — removing a member just drops that user's sudo, since it is group-based).

usr_block-binary-<name>

Prevent members from running <name> on any enrolled host via a PATH-priority wrapper that checks group membership at runtime. Use __ for . to block a Flatpak (usr_block-binary-org__gimp__Gimp). Non-members are unaffected (the wrapper transparently execs the real binary). Deleting the IPA group removes the wrapper.

usr_prt_<printer>

Auto-add <printer> to CUPS on any host where a member is logged in. Store the printer URI in the group description:

ipa group-add usr_prt_frontdesk --desc='ipps://printserver/printers/frontdesk'
ipa group-add-member usr_prt_frontdesk --users=alice

Requires cups (installed by the policy deploy). Leaving/deleting removes the printer.

usr_smb_r_<name> / usr_smb_rw_<name>

Auto-mount a Samba share at ~/<name> for logged-in members (rw wins if a user is in both). Store the credential in the group description (written by ansipa-smb-setup.sh for the built-in shares):

cifs://<ipa-host>:<samba-user>:<password>

Requires cifs-utils (installed by the policy deploy). Leaving unmounts on the next run.

usr_smb_adm_policystore

Mount the policy store at ~/policystore (rw) for admin members, so you can edit the live policies.d/*.sh files directly on the share — changes take effect fleet-wide on the next 30-min enforcer tick, no redeploy needed. Credential in the group description (auto-set by ansipa-smb-setup.sh).

usr_ctl_dnshostfile

Grants members write access to /etc/hosts via a POSIX ACL (setfacl -m u:<user>:rw /etc/hosts) on every enrolled host. Unlike the other usr_* grants above, this doesn't rely on runtime SSSD/PAM resolution — a filesystem ACL can't check group membership dynamically at the point of use, so the enforcer computes the group's actual member list (ipa group-show usr_ctl_dnshostfile --all) and re-applies it every tick, revoking the ACL entry for anyone who has left.

  • Scope: /etc/hosts only — deliberately not /etc/resolv.conf, which on systemd-resolved systems is usually a symlink to a tmpfs stub regenerated on boot/network changes, so a static ACL there would silently vanish.
  • Requires: the acl package (setfacl/getfacl) on the client.
  • Security note: this is a real grant, not a cosmetic one — a member can redirect any hostname to any IP on that machine, enough to locally spoof a login page or update server. Grant deliberately.

usr_ctl_netman / usr_ctl_wifi

Grant members the ability to modify this device's NetworkManager connections via NetworkManager's own per-connection connection.permissions mechanism — NOT a blanket sudo/root grant. NetworkManager already lets a connection's designated non-root "permitted user" modify/activate that connection without a password (org.freedesktop.NetworkManager.settings.modify.own, allowed for local users by default on every mainstream distro's polkit rules); these policies just add the member to that list.

  • usr_ctl_netman — every non-Wi-Fi connection (ethernet, bridge, bond, VLAN, …): IP address, gateway, DNS, routes. Wi-Fi connections are left completely untouched.
  • usr_ctl_wifi — every Wi-Fi connection only: SSID, security/PSK, etc. Wired/other connections are left completely untouched.
  • A user can be in either, both, or neither — the two are kept deliberately separate rather than one combined "network admin" group.
  • Like usr_ctl_dnshostfile, membership is resolved to concrete usernames (ipa group-show ... --all) and re-applied every enforcer tick, since NetworkManager's connection permissions are static profile metadata, not something checkable dynamically at the point of use.
  • Requires: NetworkManager (nmcli) on the client.
  • Applies to connections that exist at enforcement time; a brand-new connection created afterwards is picked up on the next 30-min tick. Users can already create/manage their own new connections by default (standard NetworkManager multi-user behavior) — these policies are specifically about granting access to existing, previously root-owned connections.

BaseUser sync (BaseUser group)

Not a policy script — a systemd.path unit (deployed by deploy-baseuser-sync.yml) that, on login, adds any member of the FreeIPA BaseUser group to the local baseusers group. Use it to grant a baseline local group to all standard users.


Common recipes

Onboard a new workstation (identity + monitoring + a couple of apps):

H=ws-42.corp.example.com
ipa hostgroup-add-member dev_mon_base     --hosts=$H
ipa hostgroup-add-member dev_mon_clamscan --hosts=$H
ipa hostgroup-add-member dev_mod_anti-malware --hosts=$H   # installs clamav/rkhunter
ipa hostgroup-add-member dev_mod_docker   --hosts=$H
ipa hostgroup-add-member dev_pkg_htop     --hosts=$H
# then, on the box (or wait 30 min):
sudo /usr/local/bin/ansipa-enforce-policies.sh
sudo /usr/local/bin/ansipa-install-modules.sh

Give a helpdesk user sudo everywhere, block a risky tool:

ipa group-add-member usr_admin --users=bob
ipa group-add usr_block-binary-tcpdump && ipa group-add-member usr_block-binary-tcpdump --users=intern

Edit policy logic live (as an admin in usr_smb_adm_policystore):

$EDITOR ~/policystore/dev_mon_clamscan.sh    # applied fleet-wide next enforcer tick

Troubleshooting

Symptom Check
Policy not applying sudo /usr/local/bin/ansipa-enforce-policies.sh on the client; read the [ansipa-policies] discovery line — is the group listed?
Host not in CheckMK Is it in dev_mon_base? Is CMK_ADVERTISED_URL client-routable? ipa hostgroup-show dev_mon_base description present?
ClamAV shows "no scan yet" Scans run 02:00; trigger once with sudo /usr/local/bin/ansipa-clamscan.sh. Tools installed via dev_mod_anti-malware?
Package group ignored dev_pkg_* uses the installer timer, not the enforcer — run sudo /usr/local/bin/ansipa-install-packages.sh.
Enforcer error ipa: command not found The host is not enrolled — run freeipa-enroll.sh first.
Changes take up to 30 min That's the timer cadence; run the script manually to apply now.

State for all policies lives under /var/lib/ansipa-policies/; logs go to the journal under the ansipa-policies tag (journalctl -t ansipa-policies).