Dotfiles/docs/md/ansipa-setup.md

329 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# ansipa Setup Guide
**ansipa** = **Ans**ible + Free**IPA**: 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](freeipa-ansible.md).
---
## 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](#clients-behind-nat)).
---
## Bringing up the server stack
```bash
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):
```bash
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):
```bash
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):
```nft
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):
```nginx
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):
```caddy
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/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
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
```bash
# 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
```bash
# 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
```