329 lines
14 KiB
Markdown
329 lines
14 KiB
Markdown
# 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 proxy’s 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 agent’s `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
|
||
```
|