388 lines
18 KiB
Markdown
388 lines
18 KiB
Markdown
# 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](ansipa-setup.md); for the
|
|
architecture and container internals see [FreeIPA & Ansible](freeipa-ansible.md).
|
|
|
|
---
|
|
|
|
## 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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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).
|
|
|
|
```bash
|
|
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 `.`:
|
|
|
|
```bash
|
|
# 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:**
|
|
|
|
```bash
|
|
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):
|
|
```bash
|
|
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:**
|
|
```bash
|
|
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`):
|
|
```bash
|
|
$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`).
|