Dotfiles/setup/modules/FreeipaAnsible/ansible/ansipa-enforce-policies.sh

159 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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-<unit> Ensure <unit> is enabled and running (systemctl enable --now).
# Leaving the group reverts: service is disabled and stopped.
# dev_daemon-disable-<unit> Ensure <unit> is disabled and stopped (systemctl disable --now).
# Leaving the group reverts: service is re-enabled and started.
# <unit> 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_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-<username> Grant <username> 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_<userid> Distribute <userid>'s SSH public keys from their IPA profile to
# /home/<userid>/.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 <userid> --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_clamscan Run a daily ClamAV + rkhunter + chkrootkit scan (02:00) and report
# the result to CheckMK via a local check (Ansipa_ClamScan). Requires
# dev_mod_anti-malware (tools) and dev_mon_base (agent that collects
# the check). Leaving the group removes the scan + check.
# 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-<name> Prevent members of this FreeIPA user group from running <name>
# 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_<printer> Auto-add <printer> 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:<username>); 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_smb_r_<name> Mount a read-only Samba share as ~/name for members on any
# usr_smb_rw_<name> 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://<host>:<user>:<pass>).
# 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 —
# an editing convenience only. Changes made there are NOT picked up
# automatically: commit and push (GPG-signed) to the ansipa-policy
# git server, which ansipa-pull-apply.sh verifies and distributes
# fleet-wide. See docs/md/freeipa-ansible.md.
# usr_ctl_dnshostfile Grants members write access to /etc/hosts via a POSIX ACL
# (setfacl). Unlike the grants above, membership can't be checked
# dynamically at the point of use (a filesystem ACL isn't a
# runtime SSSD/PAM check), so the enforcer resolves the group's
# actual member list every tick and revokes the ACL entry for
# anyone who has left. Requires the 'acl' package.
# usr_ctl_netman Grants members modify access to this host's NON-Wi-Fi
# NetworkManager connections (IP/gateway/DNS/routes) via
# NetworkManager's own connection.permissions mechanism — NOT sudo.
# Wi-Fi connections are left completely untouched (see
# usr_ctl_wifi). Requires NetworkManager (nmcli).
# usr_ctl_wifi Grants members modify access to this host's Wi-Fi
# NetworkManager connections only (SSID/security) — the mirror of
# usr_ctl_netman; wired/other connections are untouched. A user
# can be in either, both, or neither. Requires NetworkManager (nmcli).
# usr_mon_logins (CheckMK) Local check for failed LOCAL login attempts in the
# last 24 h — console/tty, graphical greeters: anything via PAM
# that isn't sshd or sudo (those are usr_mon_sshlogins and
# usr_mon_rightsviolations, below).
# Reports 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.
# usr_mon_sshlogins (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.
# usr_mon_rightsviolations (CheckMK) Local check for authorization/access-control denials
# by an already-authenticated user in the last 24 h: sudo denials
# (every distro), plus SELinux AVC denials (Fedora/RHEL, if
# enforcing) and AppArmor denials (Debian/Ubuntu, if loaded) —
# each source degrades gracefully when not applicable to the host.
# WARN: ≥3 total; CRIT: ≥10 total.
# Installed fleet-wide when the group exists; requires dev_mon_base.
# usr_mon_iploc (CheckMK) Local check reporting this device's current public IP
# and geolocation (via api.ipify.org + ip-api.com — both free,
# no-API-key third-party services); WARNs when the observed
# country changes since the last reading (e.g. a device normally
# in one country suddenly showing up elsewhere). Reports the
# network EGRESS location — a VPN's exit country, not necessarily
# the device's physical location. Self-throttles to one real
# lookup per 30 min regardless of how often CheckMK polls.
# 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 are sourced from /run/ansipa/current/policies.d when the ANSIPA
# signed git-pull system (ansipa-pull-apply.sh) has populated it — every commit
# there has been GPG-verified and checked for fast-forward/no-rollback before
# landing. Falls back to the Ansible-deployed /usr/local/lib/ansipa/policies.d
# baseline otherwise (see lib/ansipa-policy.sh POLICY_DIR resolution).
# To ship a policy change fleet-wide: edit under version control, sign the
# commit, and push to the ansipa-policy git server — NOT the old SMB share.
set -euo pipefail
# shellcheck source=lib/ansipa-policy.sh
source /usr/local/lib/ansipa/policy.sh
HOST_FQDN=$(ansipa_fqdn)
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 "Applying policies from $POLICY_DIR..."
for _p in "$POLICY_DIR"/*.sh; do
[[ -f "$_p" ]] || continue
# shellcheck disable=SC1090
source "$_p"
done
log "Policy enforcement complete."