Fixes found during a complete archiso → FreeIPA DC → client enrollment →
ansipa policy deployment test cycle (Rocky 9 client VM, FreeIPA in Docker):
arch-autoinstall.sh:
- Add openssh to pacstrap — was missing, breaking headless/automated installs
- Enable sshd at boot and set PermitRootLogin yes for post-install access
ansipa-install-packages.sh:
- Fix broken uninstall logic: three always-true conditions caused every package
removal to be silently skipped; replace with a state-file-based approach that
tracks packages installed by ansipa and removes only those when the IPA group
disappears
ansipa-enforce-policies.sh:
- Add usr_admin policy: creates /etc/sudoers.d/ansipa-usr-admin granting full
sudo to the FreeIPA usr_admin user group on every enrolled host; reverted
when the IPA group is deleted
- Add usr_prt_<printer> policy: auto-adds CUPS printers for FreeIPA user group
members at login; printer URI stored in IPA group description; per-user ACL;
reverted when group is deleted or membership ends
deploy-ansipa-policies.yml:
- Document usr_admin and usr_prt_* policies in header comment
- Install cups package on clients (required for printer policy)
FreeIPA container (image/):
- Add docker-env.service: extracts IPA_*/SMB_*/LUKS_*/KEYCLOAK_* env vars from
/proc/1/environ into /etc/container.env on container start; services read from
there rather than relying on PassEnvironment (which is lost on container restart)
- Add run.sh: wrapper that starts FreeIPA with --cgroupns host, required on cgroup
v2 hosts (WSL2, recent Linux) because Docker Compose schema does not expose
cgroupns_mode; also wires LUKS_KEY_UPLOAD_PASSWORD which the SMB service needs
- Dockerfile: copy + enable docker-env.service; add glibc-langpack-en for locale
- ansipa-smb.service: use EnvironmentFile=/etc/container.env (via docker-env.service)
instead of PassEnvironment; add docker-env.service dependency
- ipa-first-boot.service: add docker-env.service dependency + EnvironmentFile
- ipa-first-boot.sh: add --skip-mem-check (container has limited cgroup memory
visibility); remove --no-reverse from non-DNS path (was invalid without DNS)
- docker-compose.yml: add prominent cgroupns note explaining when to use run.sh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
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>