Dotfiles/docs/md/ansipa-setup.md

14 KiB
Raw Blame History

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

Firewall

All ansipa traffic is client-initiated, so the client firewall needs almost nothing open inbound; the server firewall is where you open ports.

Server — inbound (open these)

Minimum for identity + policy enforcement:

Port(s) Proto Why
389, 636 TCP LDAP / LDAPS — SSSD directory lookups
88, 464 TCP+UDP Kerberos KDC + kpasswd
443 TCP FreeIPA web UI + enrollment (also behind the gateway)

Add per feature you use:

Port(s) Proto Feature
137-138 UDP SMB (policy store, scan upload, LUKS keys)
139, 445 TCP SMB
8090 TCP CheckMK web UI
8000 TCP CheckMK agent receiver — required for NAT/push clients
6557 TCP CheckMK livestatus (only if external tools query it)
8080, 8443 TCP Keycloak (usually only via the gateway, not exposed directly)
8088 TCP nginx gateway — the one port your reverse proxy talks to

If you front everything with the nginx gateway + your own reverse proxy, the only web port that needs to be reachable from outside is the one your reverse proxy uses to reach 8088 (or your proxys own 443). LDAP/Kerberos/SMB are still needed directly by enrolled clients and are not proxied.

Client — inbound

Port Proto When
6556 TCP only if using CheckMK pull mode (server reaches client). Not needed — and not possible — for NAT clients, which use push mode (outbound to server :8000).
22 TCP only if you manage the client over SSH

Push-mode and policy enforcement need no inbound ports on the client.

Example rules (server)

firewalld (RHEL/Fedora/Rocky):

for p in 389/tcp 636/tcp 88/tcp 88/udp 464/tcp 464/udp 443/tcp \
         137/udp 138/udp 139/tcp 445/tcp 8090/tcp 8000/tcp 8088/tcp; do
    firewall-cmd --permanent --add-port=$p
done
firewall-cmd --reload

ufw (Debian/Ubuntu):

for p in 389 636 88 464 443 139 445 8090 8000 8088; do ufw allow $p/tcp; done
for p in 88 464 137 138; do ufw allow $p/udp; done

nftables (excerpt for an inet filter input chain):

tcp dport { 389, 636, 88, 464, 443, 139, 445, 8090, 8000, 8088 } accept
udp dport { 88, 464, 137, 138 } accept

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.

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/auth in .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 use https and 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 agents cmk-agent-ctl binary. RPM/DEB distros ship it in the agent package; on Arch the portable check_mk_agent fallback lacks it, so an Arch NAT client enrolls and gets policies but needs cmk-agent-ctl installed manually for push monitoring.


Deploying as a Proxmox LXC container

To run the whole stack as a single Proxmox container, use the generator:

bash setup/tools/ansipa-proxmox-lxc.sh [OUT_DIR]   # default ~/ansipa-lxc-out

It prompts for domain/realm/VMID/resources, generates all secrets, and writes:

File Purpose
pct-create-<vmid>.sh pct create command — run on the Proxmox host
ansipa-<vmid>.conf LXC options to append to /etc/pve/lxc/<vmid>.conf
ansipa-provision.sh first-boot provisioner (installs Docker, brings the stack up)
.env filled-in stack environment (generated passwords — mode 600)
DEPLOY-<vmid>.md step-by-step guide

The container is privileged with nesting=1,keyctl=1,fuse=1 — required for FreeIPA's systemd + Kerberos keyrings and Docker's overlay2. It runs the same tested docker-compose stack inside the CT ("Docker-in-LXC"), so it is the full ansipa deployment in one Proxmox container. Size it ≥6 GB RAM.

setup/tools/freeipa-image.sh has a separate proxmox-lxc target that exports a FreeIPA-only CT rootfs (no CheckMK/Keycloak/nginx) — use that if you want just the identity server; use ansipa-proxmox-lxc.sh for the full stack.


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