11 KiB
ansipa Setup Guide
ansipa = Ansible + FreeIPA: a GPO-like management plane for Linux fleets. FreeIPA provides identity, Kerberos, sudo/HBAC and host/user groups; a lightweight client-side enforcer turns group membership into enforced policy; CheckMK monitors the fleet; Keycloak adds SSO; and an nginx gateway fronts all the web UIs behind one hostname.
This guide covers a full deployment: architecture, every port, the reverse-proxy gateway, the exact snippet to expose it behind your own reverse proxy, the NAT / behind-firewall behaviour, and known distro caveats. For the policy catalogue and playbook reference see FreeIPA & Ansible.
Architecture
┌─────────────────────────── docker host ───────────────────────────┐
│ docker network ipa-net (172.30.0.0/24) │
your reverse │ │
proxy (TLS) │ ┌── ansipa-nginx (gateway) ──┐ │
│ │ │ / portal start page │ │
https://ansipa ──┼──►│ /ipa/ ─► freeipa :443 ───┼──► 172.30.0.10 FreeIPA │
.example.com │ │ /cmk/ ─► checkmk :5000 ──┼──► 172.30.0.12 CheckMK │
(:443) │ │ /auth/ ─► keycloak :8080 ──┼──► keycloak Keycloak │
│ └────────────────────────────┘ │ │
│ :8088 (http) └─ postgres (KC backend) │
└────────────────────────────────────────────────────────────────────┘
▲ ▲
client-initiated only │ (enroll, kinit, policy pull, │
— works across NAT │ CheckMK push :8000) │
┌────────────────┴───────────┐ ┌──────────────┴─────────────┐
│ enrolled client (Arch) │ │ enrolled client (Fedora…) │
│ ansipa-enforce-policies │ │ SSSD + Kerberos + enforcer │
│ .timer (every 30 min) │ │ │
└─────────────────────────────┘ └────────────────────────────┘
The design is pull-based: the server never initiates connections to clients.
Every enrolled host runs ansipa-enforce-policies.sh on a 30-minute timer, does
kinit -k host/$FQDN, reads its own group memberships from FreeIPA, and applies
the matching policies locally. This is what makes the whole system work when
clients are behind NAT (see Clients behind NAT).
Bringing up the server stack
cd setup/modules/FreeipaAnsible/image
cp .env.example .env && $EDITOR .env # set domain, realm, passwords, ports
# cgroup v2 hosts (WSL2, modern Fedora/Ubuntu/Arch): use the run.sh wrapper —
# the FreeIPA container needs --cgroupns host, which compose cannot express.
./run.sh all # freeipa + postgres + keycloak + checkmk + nginx gateway
# ./run.sh # freeipa only
# ./run.sh down # stop everything
# plain cgroup v1 hosts can use compose directly:
docker compose up -d
docker compose logs -f freeipa # watch first-boot (~10 min)
./keycloak-configure.sh # wire Keycloak → FreeIPA LDAP (once healthy)
Ports
Server (published on the docker host)
| Port | Proto | Service | Notes |
|---|---|---|---|
| 389 | TCP | LDAP | FreeIPA directory |
| 636 | TCP | LDAPS | FreeIPA directory (TLS) |
| 88 | TCP+UDP | Kerberos | KDC |
| 464 | TCP+UDP | kpasswd | password changes |
| 443 | TCP | HTTPS | FreeIPA web UI (also proxied at /ipa/) |
| 445 | TCP | SMB | Samba shares (scans, LUKS keys, policy store) |
| 137 | UDP | NetBIOS name | Samba |
| 138 | UDP | NetBIOS datagram | Samba |
| 139 | TCP | NetBIOS session | Samba |
| 8090 | TCP | CheckMK web UI | container :5000 → host 8090 (also proxied at /cmk/) |
| 8000 | TCP | CheckMK agent receiver | push-mode target for NAT clients |
| 6557 | TCP | CheckMK livestatus | optional, external tooling |
| 8080 | TCP | Keycloak HTTP | dev mode (also proxied at /auth/) |
| 8443 | TCP | Keycloak HTTPS | needs a cert for the start command |
| 8088 | TCP | nginx gateway | single entry for the portal + all UIs (ANSIPA_HTTP_PORT) |
Client (only needed for CheckMK pull mode)
| Port | Proto | Service | Notes |
|---|---|---|---|
| 6556 | TCP | check_mk agent socket | server → client; unreachable across NAT, use push mode instead |
| 22 | TCP | sshd | if the client is in dev_daemon-enable-sshd and you manage over SSH |
The nginx gateway (portal + reverse proxy)
The ansipa-nginx service fronts all three web UIs on one hostname/port and
serves a portal landing page. Each backend is proxied at the path it already
uses natively, so there is no path rewriting to fight:
| Path | Backend | Notes |
|---|---|---|
/ |
portal start page | static, links to the three UIs |
/ipa/ |
FreeIPA :443 |
Host + Referer pinned to the IPA hostname (IPA anti-CSRF) |
/cmk/ |
CheckMK :5000 |
CheckMK already serves under /cmk |
/auth/ |
Keycloak :8080 |
Keycloak runs with KC_HTTP_RELATIVE_PATH=/auth |
The gateway speaks plain HTTP on ANSIPA_HTTP_PORT (default 8088) and trusts
X-Forwarded-*, so it is designed to sit behind your own TLS-terminating
reverse proxy.
Exposing ansipa behind your main reverse proxy
Point one hostname at the gateway. TLS is terminated by your proxy; ansipa
receives plain HTTP and reads X-Forwarded-Proto to build correct URLs.
nginx (add to your main reverse proxy):
server {
listen 443 ssl;
server_name ansipa.example.com;
# ssl_certificate / ssl_certificate_key ...
location / {
proxy_pass http://DOCKER_HOST_IP:8088;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
}
}
Caddy (one line):
ansipa.example.com {
reverse_proxy DOCKER_HOST_IP:8088
}
Traefik (docker labels on the nginx service): route the host rule to the
ansipa-nginx service on port 80.
Per-app hostname settings (recommended for production)
Both CheckMK and Keycloak generate some absolute URLs from their own configured hostname rather than the forwarded one. For a clean deployment behind a public name, set:
- Keycloak:
KC_HOSTNAME=https://ansipa.example.com/authin.env. - CheckMK: set the site's URL prefix / base URL to your public
https://ansipa.example.com/cmk/(OMD → Global settings → User interface → Base URL), so login redirects usehttpsand the public host.
Without these the UIs are still reachable through the proxy; only some redirects fall back to the container-internal scheme/host.
Clients behind NAT
The controller can live on the public internet while clients sit behind NAT — the architecture is client-initiated end to end:
| Function | Direction | Works behind NAT? |
|---|---|---|
Enrollment (ipa-client-install) |
client → server | ✅ |
| Policy enforcement (30-min timer) | client → server (kinit + ipa reads) |
✅ |
| Policy store sync | client → server SMB | ✅ |
CheckMK pull (:6556) |
server → client | ❌ — blocked by NAT |
CheckMK push (cmk-agent-ctl → :8000) |
client → server | ✅ |
So policies and identity Just Work across NAT. For monitoring, NAT clients must
use push mode: the server records a push request for each host in
dev_mon_base (ansipa-checkmk-setup.sh writes /cmk-creds/push-mode-<host>.req,
the CheckMK container consumes it and flips the host to cmk_agent_connection: push-agent), and the client registers with cmk-agent-ctl register --server <host>:8000. Advertise a client-routable CheckMK URL so agents can reach the
server — set CMK_ADVERTISED_URL in .env (e.g. http://mon.example.com:8090);
it is stored in the dev_mon_base group description and must NOT be the
container-internal 172.30.0.12.
Note: push mode needs the full CheckMK agent’s
cmk-agent-ctlbinary. RPM/DEB distros ship it in the agent package; on Arch the portablecheck_mk_agentfallback lacks it, so an Arch NAT client enrolls and gets policies but needscmk-agent-ctlinstalled manually for push monitoring.
Client enrollment
# on the client, as root:
sudo ./freeipa-enroll.sh --domain ansipa.example.com --realm ANSIPA.EXAMPLE.COM \
--server ipa.ansipa.example.com --hostname client01.ansipa.example.com
# (IPA_PASSWORD env var or --password; --no-dns-update if IPA does not run DNS)
Supported distros: Arch, Fedora, RHEL/Rocky/AlmaLinux, Debian/Ubuntu. The script picks the right package manager and package names automatically.
Arch caveat — autofs / freeipa-client from the AUR
On Arch, ipa-client-install comes from the AUR freeipa-client package, which
pulls the entire FreeIPA suite plus autofs from source — a long build
(tens of minutes on a small VM). ansipa bundles its own autofs-pkgbuild/ that
builds autofs from upstream git, because the stock AUR autofs currently fails
(its pinned autofs-5.1.9.tar.xz was pruned from kernel.org). freeipa-enroll.sh
builds and installs it automatically before the AUR freeipa-client step, so no
manual intervention is needed — just expect the first enrollment to take a while.
Verifying a deployment
# server
docker compose ps # all healthy
curl -fsS http://DOCKER_HOST_IP:8088/ | grep ansipa # portal
# client (after enrollment + one enforcer run)
id admin # SSSD resolves IPA users
kinit admin && klist # Kerberos works
sudo /usr/local/bin/ansipa-enforce-policies.sh # apply policies now
sudo systemctl list-timers ansipa-enforce-policies.timer