Runbook 10 — FreeIPA server build (identity: LDAP/Kerberos/DNS/CA)¶
Goal: stand up FreeIPA in SERVERS as the identity backbone — consistent
UID/GID, Kerberos, an internal CA, and (optionally) integrated DNS. This unlocks
SSO and sec=krb5 NFS in runbooks 11–12.
Prereqs: runbooks 03–06 (SERVERS zone + DNS + rules). A fresh VM/host for FreeIPA (do not put it on the fileserver or the firewall).
Decisions (set before installing)¶
- Realm / domain: use a dedicated subdomain to avoid clashing with your public
${domain}DNS — domain${ipa_domain}, realm${realm}(realm = uppercase of the domain, per Kerberos convention). Cleaner separation from the public zone (origin Caddy + hidden Bind primary, fronted by the VPS edges). - DNS: let FreeIPA run its integrated DNS for the IPA domain only;
Unbound forwards
${ipa_domain}to it (runbook 04 step 1). FreeIPA is not your main resolver — Unbound stays that. - Host: AlmaLinux 9 — same template as the rest of the fleet (the whole lab
standardized on EL, partly because FreeIPA is a Red Hat project and runs best
here). 2 vCPU / 4 GB / 20 GB. Clone per runbook 01 (NIC
tag=20SERVERS). - IP: static
10.20.20.2in SERVERS (gw10.20.20.1, DNS during install = itself once up; bootstrap with10.20.20.1).
1. Prepare the host¶
# static hostname must be the FQDN
hostnamectl set-hostname ipa.${ipa_domain}
# /etc/hosts: pin the FQDN
echo "10.20.20.2 ipa.${ipa_domain} ipa" >> /etc/hosts
dnf -y update
dnf -y install ipa-server ipa-server-dns
# open the firewall on the host (firewalld)
firewall-cmd --permanent --add-service={freeipa-ldap,freeipa-ldaps,dns,ntp}
firewall-cmd --reload
Ensure forward/reverse name resolution of the FQDN works before installing.
2. Install FreeIPA¶
ipa-server-install \
--realm=${realm} \
--domain=${ipa_domain} \
--ds-password='<DM-password>' \
--admin-password='<admin-password>' \
--setup-dns \
--forwarder=10.20.20.1 \
--no-reverse \
--ssh-trust-dns
--forwarder=10.20.20.1 sends non-IPA lookups back to Unbound on the firewall.
- Accept the chrony/NTP setup (time sync is mandatory for Kerberos).
- Note the CA cert location: /etc/ipa/ca.crt (you'll trust this on clients and
optionally in OPNsense).
Verify:
kinit admin # get a Kerberos ticket
ipa user-find # talks to the directory
3. Wire DNS to Unbound¶
On OPNsense (Services → Unbound DNS → Query Forwarding → +):
- Domain: ${ipa_domain} → Server IP 10.20.20.2
- (and the reverse zone if you later enable --reverse)
So clients resolving *.${ipa_domain} (IPA-enrolled hosts/services) hit FreeIPA,
everything else stays with Unbound.
4. Create users / groups / homedir policy¶
# group for lab users
ipa group-add labusers --desc "Lab users"
# ADMIN group — the whole SSO/authz plane keys off this one group (create it now)
ipa group-add fleet-admins --desc "Lab administrators"
# a user (UID/GID auto-allocated consistently across all enrolled hosts)
ipa user-add operator --first Lab --last Operator --email admin@example.com
ipa group-add-member labusers --users=operator
ipa group-add-member fleet-admins --users=operator # grant the operator admin
ipa passwd operator
fleet-adminsis the lab's single admin group — create it here, before anything downstream. Membership is surfaced via the LDAPgroupsclaim and mapped to admin across the plane: Authelia's admin group (runbook 13), Grafana admin, the control-plane Forgejo site admin, the Jellyfin admin filter, andipa_accessHBAC/sudo (which assumes the usergroup already exists). Add/remove an admin = oneipa group-add-member fleet-adminshere; there is no per-app admin list to keep in sync.
5. Firewall alias update¶
Set the FREEIPA host alias to 10.20.20.2 and confirm IPA_PORTS
(88,464,389,636,53,443) are allowed WS→SERVERS (runbook 06).
5b. Trust the IPA CA across the fleet¶
Now that FreeIPA's CA exists, have the Ansible common role trust it everywhere:
# copy the CA off the FreeIPA server, then on the control node:
cp ipa-ca.crt ansible/roles/common/files/ipa-ca.crt # CA = /etc/ipa/ca.crt
# set lab_ipa_ca_file: ipa-ca.crt (defaults or host_vars), then re-run:
cd ansible && ansible-playbook playbooks/site.yml
6. Nightly logical backup¶
Image backups (Proxmox/ZFS daily) are crash/fs-consistent; add an
app-consistent dump. The db_backups Ansible role (runbook 16)
installs a systemd timer running ipa-backup --data --online nightly to
/var/lib/ipa/backup (captured by the daily image backup), with retention.
cd ansible && ansible-playbook playbooks/db-backups.yml --limit freeipa
ipa-backup on a maintenance window for DR.
Validation¶
- [ ]
kinit adminsucceeds;ipa user-findlists users. - [ ]
systemctl list-timers db-backup.timershows it scheduled; a manualsystemctl start db-backup.serviceproduces anipa-data-*backup. - [ ] From a WORKSTATIONS test client:
dig ipa.${ipa_domain}resolves (via Unbound→IPA forward) anddig google.comstill resolves (via Unbound). - [ ] FreeIPA web UI reachable at
https://ipa.${ipa_domain}from an allowed zone; not reachable from GUEST/IOT/DMZ. - [ ] Chrony/time healthy (Kerberos is time-sensitive).
Then → runbook 11 (AlmaLinux ZFS + NFS storage).