#!/usr/bin/env bash # ansipa-enforce-policies.sh — enforce FreeIPA host-group-driven policies on this client. # # Policies are idempotent and reversible: joining a group applies the policy; # leaving the group removes it on the next run (every 30 min via systemd timer). # # Host-group naming conventions (device policies — applied to the whole machine): # dev_daemon-enable- Ensure is enabled and running (systemctl enable --now). # Leaving the group reverts: service is disabled and stopped. # dev_daemon-disable- Ensure is disabled and stopped (systemctl disable --now). # Leaving the group reverts: service is re-enabled and started. # may omit the .service suffix; all systemd unit types work. # If a unit appears in both enable and disable groups it is skipped. # dev_timeshift-backup Enforce a daily Timeshift snapshot (requires timeshift installed) # dev_security-scan Enforce daily ClamAV + rkhunter + chkrootkit scans # dev_no-local-users Lock passwords for root and all local users (UID >= 1000) so # that only FreeIPA domain accounts with centrally-managed sudo # rules can authenticate and gain elevated privileges. # Leaving the group reverts: every account locked by this policy # is unlocked on the next run. # dev_local-sudo- Grant full sudo on this specific device by adding # them to the local sudoers drop-in. Leaving the group removes # the drop-in on the next run. # dev_ssh_ Distribute 's SSH public keys from their IPA profile to # /home//.ssh/authorized_keys on this device (ansipa-managed # section; manually added keys are preserved). Leaving the group # removes only the ansipa-managed section on the next run. # Keys stored in IPA via: ipa user-mod --sshpubkey='...' # # CheckMK monitoring host-groups (device policies — require dev_mon_base first): # dev_mon_base Install CheckMK agent; register host in CheckMK under /ansipa; # enable check_mk.socket (port 6556) for pull mode, or # cmk-agent-ctl push timer for push mode (when server can't # reach the client directly). # Built-in sections report CPU, RAM, disk, uptime. # Custom check: installed package count (rpm / dpkg / pacman). # Credentials are auto-discovered from the IPA dev_mon_base # description (populated by ansipa-checkmk-setup.sh on the server). # Leaving the group deregisters the host and disables the agent. # dev_mon_malware Local check reporting ClamAV scan results from the # dev_security-scan log. Requires dev_security-scan to run first; # both groups can coexist independently. # dev_mon_timeshift Local check for most-recent Timeshift snapshot age. # WARN if >5 days, CRIT if >10 days; CRIT if no snapshots found. # dev_mon_power Local check for CPU package power via Intel RAPL (preferred) # or lm-sensors fallback. WARN >65 W, CRIT >95 W. # # User-group naming conventions (per-user policies — follow the user across devices): # usr_admin Members get full sudo on every enrolled host via a sudoers # drop-in (/etc/sudoers.d/ansipa-usr-admin). Applied while the # FreeIPA user group exists; drop-in is removed when the group is # deleted. Uses SSSD so group membership resolves without local # accounts. # usr_block-binary- Prevent members of this FreeIPA user group from running # on any enrolled host. Use __ in place of . to support Flatpak # app IDs (e.g. usr_block-binary-org__gimp__Gimp blocks the # Flatpak org.gimp.Gimp). Enforced via a PATH-priority wrapper # that checks group membership at runtime via SSSD/id(1). # Removing the user group from FreeIPA reverts the wrapper. # usr_prt_ Auto-add to CUPS on any enrolled host where a member # is logged in. The printer URI is stored in the IPA group # description (e.g. ipps://host/printers/name). Access is # per-user only (allow:); each new member who logs in # gets added to the allow list. When the IPA group is deleted # or all members leave, the printer is removed from CUPS on that # host. A profile.d snippet + NOPASSWD sudo rule trigger the # enforcer at login for immediate setup. # usr_scan-notify Members receive scan alert notifications on every enrolled device # they log into. The fetch-alerts timer is installed fleet-wide # when the group exists; the notification daemon starts on login # only for group members (checked via id(1) / SSSD). # Deleting the IPA user group removes the timer and profile.d # snippet on the next enforcer run. # usr_smb_r_ Mount a read-only Samba share as ~/‹name› for members on any # usr_smb_rw_ enrolled host where they are logged in. rw membership takes # precedence over r when both exist for the same share name. # Credential string is stored in the IPA group description by # ansipa-smb-setup.sh (cifs://::). # Leaving the group unmounts the share on the next run. # Requires: cifs-utils installed on the client. # usr_smb_adm_policystore Mount the ansipa policy store at ~/policystore (rw) for members. # Credential in IPA group description (set by ansipa-smb-setup.sh). # Also used by the enforcer itself to sync policy files from the # server before applying them — edits on the SMB share take effect # on the next 30-min enforcer tick fleet-wide. # usr_mon_logins (CheckMK) Local check for SSH login attempts in the last 24 h: # successful + failed + invalid-user counts with thresholds. # WARN: ≥10 failed or ≥5 invalid; CRIT: ≥50 failed or ≥20 invalid. # Installed fleet-wide when the group exists; requires dev_mon_base. # # Notes: # - Install scan tools first: add the host to dev_mod_anti-malware. # - Configure Timeshift (type + target device) before enabling dev_timeshift-backup. # - CheckMK server must be running and accessible before adding hosts to dev_mon_base. # Run ansipa-checkmk-setup.sh on the FreeIPA container to seed credentials and groups. # - Policy files live in /usr/local/lib/ansipa/policies.d/ on enrolled clients. # The enforcer syncs them from the ansipa-policystore SMB share before each run; # edit files on the share directly (~/policystore if you are in usr_smb_adm_policystore) # to update policies without re-running Ansible. set -euo pipefail # shellcheck source=lib/ansipa-policy.sh source /usr/local/lib/ansipa/policy.sh HOST_FQDN=$(hostname -f 2>/dev/null || hostname) command -v ipa &>/dev/null || { warn "ipa not found — host not enrolled in FreeIPA. Exiting."; exit 0; } kinit -k "host/$HOST_FQDN" &>/dev/null || true mkdir -p "$STATE_DIR" log "Discovering groups..." _ansipa_discover log "Syncing policy store..." _ansipa_sync_policystore log "Applying policies..." for _p in "$POLICY_DIR"/*.sh; do [[ -f "$_p" ]] || continue # shellcheck disable=SC1090 source "$_p" done log "Policy enforcement complete."