Commit Graph

26 Commits (21ec76c6ebf6507d47e4852b8efe18cbd91ae8ab)

Author SHA1 Message Date
Amir Alexander Abdelbaki 547c997614 feat(ansipa): rework scan-notify as per-user policy
policy-scan-notify is now a FreeIPA *user* group instead of a host group,
so alert notifications follow the user to every enrolled machine. The
fetch-alerts timer is installed fleet-wide on any host where the group exists;
the profile.d snippet gates notification daemon start on runtime group
membership (id(1) / SSSD) so non-members log in unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:41:35 +02:00
Amir Alexander Abdelbaki 87b62f368b feat(ansipa): rework binary blocking as per-user policy; add local_sudo device policy
policy-block-binary-<name> is now a FreeIPA *user* group instead of a host group,
so restrictions follow the user to every enrolled machine. The PATH wrapper is
installed on all hosts and checks group membership at runtime via id(1)/SSSD,
passing non-members through transparently. __ in the group name decodes to .
so Flatpak app IDs are supported (flatpak run fallback included). AppArmor layer
removed since per-user confinement requires a different approach and the wrapper
alone is sufficient. Adds local_sudo_<username> host group policy which writes
a sudoers drop-in granting that user full sudo on the specific device, reverted
on group leave.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:31:43 +02:00
Amir Alexander Abdelbaki 6ad8d0d488 feat(ansipa): add no_local_users device policy to lock all local account passwords
Adds a new host group policy `no_local_users` that locks the passwords of root
and all local users (UID >= 1000) via `passwd -l`, ensuring only FreeIPA domain
accounts with centrally-managed sudo rules can authenticate and gain elevated
privileges. Leaving the group reverts by unlocking every account tracked in the
state file. Updates docs with group reference entry and Local User Lockdown section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 16:18:48 +02:00
Amir Alexander Abdelbaki 5d56984e38 feat(ansipa): store LUKS backup keys on SMB share with KeyAdmin access control
ansipa-smb-setup.sh:
- Adds KeyAdmin Linux group and luks-upload service account (member of
  KeyAdmin) on the IPA container, both persisted across restarts.
- LUKS base dir /data/luks-keys owned root:KeyAdmin, mode 2750 (setgid
  so new files inherit the group).
- New [ansipa-luks-keys] SMB share: valid users = @KeyAdmin, read only,
  write list = luks-upload. Human admins gain read access by being added
  to KeyAdmin: useradd -r -G KeyAdmin <user> && smbpasswd -a <user>.
- LUKS_KEY_UPLOAD_PASSWORD sourced from env / /data/samba/ansipa-smb.env
  alongside the existing SMB_SCAN_PASSWORD.

collect-luks-keys.yml:
- After fetching /_LUKS_BACKUP_KEY from each client, uploads it to the
  ansipa-luks-keys share via smbclient using a temp credentials file
  (no_log, deleted in post_tasks).
- Local staging copy is removed after a successful upload.
- SMB credentials file uses an epoch-stamped path to avoid collisions.

.env.example: documents LUKS_KEY_UPLOAD_PASSWORD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:33:17 +02:00
Amir Alexander Abdelbaki aced2c754e feat(ansipa): add daemon enable/disable policy via host-group regex
Host groups named policy-daemon-enable-<unit> and
policy-daemon-disable-<unit> are now matched by a wildcard case arm in
the group parser — no per-service configuration required.

Enforcement (every 30 min via existing timer):
  enable:  systemctl enable --now <unit>; state written to
           /var/lib/ansipa-policies/daemon-enabled
  disable: systemctl disable --now <unit>; state written to
           /var/lib/ansipa-policies/daemon-disabled
  revert:  when a host leaves a group the opposite action is applied
           on the next run (enable→disable, disable→enable)
  conflict: unit in both lists is skipped with a warning

The .service suffix is optional — _svc_unit() appends it when the name
contains no dot, so all systemd unit types work as-is.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:25:15 +02:00
Amir Alexander Abdelbaki c56c86d57b fix(freeipa): harden container SMB setup and fetch-alerts script
ansipa-smb.service: WantedBy=multi-user.target (was smb.service) so the
  setup service always runs at boot, not only when smb.service pulls it in

docker-compose.yml: add NetBIOS UDP ports 137/138 to match Dockerfile EXPOSE
  and nmb.service being enabled

ansipa-smb-setup.sh:
  - use printf '%q' when writing SMB_SCAN_PASSWORD to ansipa-smb.env so
    passwords with spaces or shell-special chars are correctly quoted
  - always write /etc/cron.d/ansipa-check-scans (remove the [[ ! -f ]] guard)
    since /etc/cron.d is on the ephemeral container layer and is lost on
    container recreation; the service runs on every start anyway

Dockerfile: add -e SMB_SCAN_PASSWORD and -p 445:445 to the quick-test comment

ansipa-fetch-alerts.sh: replace $NEW && log with [[ "$NEW" == true ]] && log
  to avoid set -e ambiguity with the 'false' builtin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 13:13:53 +02:00
Amir Alexander Abdelbaki 11e66dbddd feat(freeipa): scan result reporting, alert notifications, and SMB share
Container (ansipa image):
- Add samba + cronie to Dockerfile; expose ports 445/139
- ansipa-smb-setup.sh: idempotent setup of smbd + scanupload user +
  /data/scan-results/{archive,alerts}/ on every container start
- ansipa-smb.service: runs setup before smb.service on each boot
- ansipa-check-scans.sh: hourly cron on server; analyses archive logs for
  ClamAV/rkhunter/chkrootkit findings and writes <host>/<date>.alert files
- docker-compose.yml: add SMB_SCAN_PASSWORD env var + port mappings
- .env.example: document SMB_SCAN_PASSWORD

Client (policy-security-scan):
- Scan script now uploads log to //ipa-server/ansipa-scans/archive/<host>/
  via smbclient after each run

Client (policy-scan-notify — new policy group):
- ansipa-fetch-alerts.sh: root timer (10 min) downloads alerts from SMB into
  ~/administration/<hostname>/ for each active login session; deletes server
  alert when user removes local file (acknowledgment)
- ansipa-scan-notify.sh: user daemon started via /etc/profile.d/ansipa-notify.sh;
  sends notify-send every 10 min while *.alert files remain in ~/administration/
- deploy-ansipa-policies.yml: installs samba-client, deploys SMB creds file
  (/etc/ansipa-smb.creds, 0600), and deploys both notification scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:32:21 +02:00
Amir Alexander Abdelbaki fb8ca498ef feat(freeipa): add AppArmor deny profiles to binary blocking policy
Binary blocking now applies two layers:
  1. PATH-priority wrapper in /usr/local/bin/ (existing)
  2. Empty AppArmor profile in /etc/apparmor.d/ loaded in enforce mode

An empty AppArmor profile denies all access — the blocked binary cannot
load shared libraries and exits immediately with a permission error,
covering callers that use absolute paths and bypassed the wrapper.

AppArmor layer is skipped silently when apparmor_parser is not present,
and deferred with a warning if the real binary is not yet installed.
Profiles are unloaded and deleted when the host leaves the policy group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 12:00:55 +02:00
Amir Alexander Abdelbaki 45fd7e5d36 feat(freeipa): add policy enforcement for binary blocking, backups, scans, and sudo
Introduces a FreeIPA host-group-driven policy system alongside a sudo
rules management playbook:

- ansipa-enforce-policies.sh: client-side enforcer (systemd timer, 30 min)
  - policy-block-binary-<name>: PATH-priority wrapper blocks the binary
  - policy-timeshift-backup: daily Timeshift snapshot cron (03:00)
  - policy-security-scan: daily ClamAV/rkhunter/chkrootkit cron (02:00)
  Policies are reversible — leaving a group removes enforcement on next run.

- deploy-ansipa-policies.yml: deploys enforcer + systemd service/timer to clients

- manage-sudo-rules.yml: creates FreeIPA sudo rules (allow_sudoers,
  allow_sudo_nopasswd) that SSSD clients already pick up via --sudo enrollment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 11:34:09 +02:00
Amir Alexander Abdelbaki f1ea6dcb54 ansible: add collect-luks-keys playbook for LUKS backup key archival
New playbook collect-luks-keys.yml connects to all enrolled FreeIPA
clients, checks for /_LUKS_BACKUP_KEY (placed there by the installer
when encryption is enabled), and fetches each key to the Ansible
controller as luks-keys/<HOSTNAME>_LUKS_BACKUP_KEY (mode 0400).

Hosts without the file are reported but not treated as errors.
The luks-keys/ store directory is created with mode 0700.

Usage:
  ansible-playbook -i inventory collect-luks-keys.yml

Can be scheduled via cron on the controller for automatic collection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:25:05 +02:00
Amir Alexander Abdelbaki fb9893504c setup: add FreeIPA Flatpak group installer (fp_install_* groups)
IPA group naming: fp_install_org__mozilla__firefox (dots encoded as __)
Decoding: sed strips prefix, then s/__/./g restores the Flatpak app ID.
Single underscores in app IDs are preserved unambiguously.

ansipa-install-flatpaks.sh:
- kinit with host keytab, queries ipa group-find --pkey-only with awk $NF
- Validates decoded ID against reverse-domain regex before installing
- Ensures flathub system remote exists
- System-scope install (flatpak install --system) since service runs as root
- Timer offset to 4 min (after packages at 2 min) to avoid contention

deploy-ansipa-install.yml updated to deploy the Flatpak script, service,
and timer alongside the existing package installer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:52:27 +02:00
Amir Alexander Abdelbaki c51af40fce setup: add freeipa-client module and FreeIPA group-based module automation
- Add freeipa-client module (sssd, cyrus-sasl-gssapi, freeipa-client AUR)
  with post-install enrollment hints; wired into tui-install.sh and
  install-modules.sh
- Add ansipa-install-modules.sh: reads IPA host groups named
  ansipa-module-<name>, applies matching module scripts via a yay wrapper
  that drops to ANSIPA_USER so AUR builds work from the root service
- Add ansipa-install-modules.service + .timer (boot + 30 min)
- Add deploy-ansipa-modules.yml Ansible playbook that deploys scripts,
  writes /etc/ansipa-modules.conf, and enables the timer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:40:51 +02:00
Amir Alexander Abdelbaki f66775ce54 setup: add FreeIPA image builder and Keycloak integration
freeipa-image-builder.sh: TUI chooser that builds a FreeIPA server image
and exports it to four target formats:
  docker      — builds via podman/docker, optional registry push
  lxc         — exports container rootfs as .tar.zst Proxmox CT template,
                 generates pct import instructions
  proxmox-vm  — downloads Rocky/Fedora cloud image, customizes with
                 virt-customize, outputs QCOW2 + cloud-init user-data.yml
  oci-archive — skopeo OCI tarball for air-gapped import

Keycloak TUI option generates the full constellation:
  docker-compose.yml   FreeIPA + Keycloak + PostgreSQL stack
  .env                 pre-filled env template (passwords placeholder)
  keycloak-configure.sh  post-start Keycloak REST API config script

image/Dockerfile: Fedora 41 + freeipa-server-dns + ansible-core,
systemd-enabled container (CMD /sbin/init).

image/ipa-first-boot.{sh,service}: systemd oneshot that runs
ipa-server-install on first container/VM boot from env vars
(IPA_DOMAIN, IPA_ADMIN_PASSWORD, IPA_DM_PASSWORD, and optionals).
ConditionPathExists=!/etc/ipa/default.conf makes it idempotent.

image/keycloak-configure.sh: Keycloak REST API automation that:
  - waits for Keycloak readiness
  - creates a realm
  - wires FreeIPA LDAP user federation (READ_ONLY, vendor=rhds)
  - adds attribute mappers: email, firstName, lastName, uidNumber
  - adds group mapper (IPA groups → Keycloak groups, cn=groups,cn=accounts)
  - triggers an initial full user sync

image/docker-compose.yml: freeipa + postgres + keycloak services on
a private 172.30.0.0/24 bridge; FreeIPA has a fixed IP so Keycloak
can resolve it via extra_hosts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:22:48 +02:00
Amir Alexander Abdelbaki 7279a781b0 setup: add FreeIPA server module and generic client script
freeipa-server.sh: interactive installer that collects domain, realm,
IP, admin/DM passwords, DNS, KRA, NTP, and AWX/Ansible settings;
runs conflict pre-flight (checks for existing named/dirsrv/krb5kdc,
ports 389/636/88, and /etc/ipa/default.conf); configures firewalld/ufw;
runs ipa-server-install; and outputs a ready-to-distribute client
package to ~/freeipa-output/ containing:
  - freeipa-enroll.sh (server defaults baked in)
  - freeipa-client.sh (server defaults baked in)
  - freeipa-client-answerfile.json (pre-filled, password intentionally blank)
  - auto-enroll-ansible.sh (AWX defaults embedded, still overridable)
  - README.txt

freeipa-client.sh: thin wrapper around freeipa-enroll.sh with three modes:
  --answerfile FILE   read JSON with jq, build args, exec freeipa-enroll.sh
  --interactive       prompt for every field, then exec freeipa-enroll.sh
  [flags]             passthrough directly to freeipa-enroll.sh

freeipa-client-answerfile.json: template with current server defaults
(freeipa.abdelbaki.eu); freeipa-server.sh sed-replaces these when
generating customized copies.

Supported server OS: RHEL/Rocky/AlmaLinux/Fedora (primary), Arch (warned).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:12:31 +02:00
Amir Alexander Abdelbaki 3890d360d6 Update setup/modules/FreeipaAnsible/copilot-explains.txt 2026-04-27 17:00:37 +02:00
Amir Alexander Abdelbaki 16732cc17c Add setup/modules/FreeipaAnsible/copilot-explains.txt 2026-04-27 16:59:36 +02:00
Amir Alexander Abdelbaki 6dbeca2bde Add setup/modules/FreeipaAnsible/ansible/ansipa-install-packages.sh 2026-04-27 16:44:59 +02:00
Amir Alexander Abdelbaki 9daf10888c Add setup/modules/FreeipaAnsible/ansible/deploy-ansipa-install.yml 2026-04-27 16:44:36 +02:00
Amir Alexander Abdelbaki 244d7385eb Add setup/modules/FreeipaAnsible/ansible/ansipa-install.timer 2026-04-27 16:44:18 +02:00
Amir Alexander Abdelbaki a2f3a03547 Add setup/modules/FreeipaAnsible/ansible/ansipa-install.service 2026-04-27 16:44:01 +02:00
Amir Alexander Abdelbaki 64af45d300 Add setup/modules/FreeipaAnsible/ansible/deploy-baseuser-sync.yml 2026-04-27 16:39:34 +02:00
Amir Alexander Abdelbaki 7d96df03ac Add setup/modules/FreeipaAnsible/ansible/baseuser-sync.path 2026-04-27 16:39:11 +02:00
Amir Alexander Abdelbaki f34cbd83b9 Add setup/modules/FreeipaAnsible/ansible/baseuser-sync.service 2026-04-27 16:38:37 +02:00
Amir Alexander Abdelbaki d927eb8904 Update setup/modules/FreeipaAnsible/auto-enroll-ansible.sh 2026-04-27 16:38:02 +02:00
Amir Alexander Abdelbaki 6d1d2c6083 Update setup/modules/FreeipaAnsible/ansible/auto-add-baseuser.sh 2026-04-27 16:37:39 +02:00
Amir Alexander Abdelbaki dea19b2998 Update setup/modules/FreeipaAnsible/freeipa-enroll.sh 2026-04-27 16:37:09 +02:00