Merge pull request 'feat(freeipa): add domain-controller setup — Rocky Linux 9 + FreeIPA 4.13' (#5) from feat/freeipa-domain-controller into main
Secret Scan / Scan for secrets and sensitive config (push) Failing after 4s

Reviewed-on: #5
This commit is contained in:
2026-07-26 21:38:46 +00:00
8 changed files with 700 additions and 0 deletions
+9
View File
@@ -9,6 +9,7 @@ service type.
``` ```
proxmox/ Proxmox VE hosts (pve1 production, pve-test sandbox) proxmox/ Proxmox VE hosts (pve1 production, pve-test sandbox)
pihole/ Pi-hole DNS/DHCP (config snapshots, pull/apply scripts) pihole/ Pi-hole DNS/DHCP (config snapshots, pull/apply scripts)
freeipa/ FreeIPA identity management server (domain-controller.sweet.home)
scripts/ Repo-wide scripts (secret scanning, git hook installer) scripts/ Repo-wide scripts (secret scanning, git hook installer)
``` ```
@@ -25,6 +26,14 @@ Pi-hole v6 configuration management. Stores a sanitised snapshot of the
live config and provides scripts to pull from or push to a running live config and provides scripts to pull from or push to a running
instance. See `pihole/README.md` for usage. instance. See `pihole/README.md` for usage.
### `freeipa/`
FreeIPA 4.x identity management server running on Rocky Linux 9
(`domain-controller.sweet.home`, VMID 108 on pve1). Provides Kerberos,
LDAP, and integrated DNS for the `sweet.home` realm. See
`freeipa/README.md` for the quick-start and `freeipa/docs/install.md`
for the full reproduction procedure.
## Secret scanning ## Secret scanning
All commits are scanned for secrets by a CI pipeline that runs on both All commits are scanned for secrets by a CI pipeline that runs on both
+33
View File
@@ -0,0 +1,33 @@
# freeipa/CLAUDE.md
Host-specific guardrails for the `domain-controller` FreeIPA server.
## Host: `domain-controller.sweet.home`
- **VMID 108 on `pve1.sweet.home`** — this is production infrastructure.
Treat it the same as any other pve1 guest: no changes without explicit
same-session operator authorisation.
- **Read-only is always fine**: SSH in as `wayne`, inspect IPA state with
`ipa *` commands or `kinit admin && ipa ...`, check service status with
`ipactl status` — none of that needs authorisation.
- **Never modify FreeIPA topology, replicas, or the LDAP DIT directly**
without the operator's go-ahead. That means no `ipa user-del`, no
`ipa-replica-manage`, no `ldapmodify` against the live directory.
- **Do not commit secrets.** The Directory Manager password and the `admin`
Kerberos password must not appear in any file in this repo. Scripts that
need them must read from environment variables or prompt interactively.
- The `admin` password and Directory Manager password were generated at
install time and stored only in the operator's password manager — not in
this repo. See `README.md` for how to retrieve/reset them.
## What is safe to run automatically
- `scripts/verify.sh` — read-only health check, no side effects.
- `scripts/configure-pihole-dns.sh` — idempotent DNS forwarder setup in
Pi-hole; safe to re-run.
## What requires operator go-ahead
- `scripts/install.sh` — destructive if run against an already-provisioned
host. Always check first with `ipactl status`.
- Any `ipa-replica-install` or `ipa-server-upgrade` invocation.
+101
View File
@@ -0,0 +1,101 @@
# FreeIPA — domain-controller.sweet.home
FreeIPA 4.x identity management server providing Kerberos, LDAP, and
integrated DNS for the `sweet.home` LAN. Runs on Rocky Linux 9 in a
Proxmox VM (VMID 108 on `pve1.sweet.home`).
## Quick status
| Item | Value |
|------|-------|
| Host | `domain-controller.sweet.home` |
| IP | `192.168.2.138` (static) |
| Realm | `SWEET.HOME` |
| Domain | `sweet.home` |
| IPA version | 4.13.x (Rocky Linux 9) |
| Web UI | `https://domain-controller.sweet.home/ipa/ui/` |
| VMID | 108 on `pve1.sweet.home` |
| OS | Rocky Linux 9 (GenericCloud image) |
## What it provides
- **Kerberos KDC** — SSO tickets for the `SWEET.HOME` realm
- **LDAP directory** — centralised user/group/host store (389-ds)
- **Integrated DNS** — authoritative for `sweet.home`, forwarding
everything else upstream to Pi-hole (`192.168.2.253`)
- **CA** — self-signed CA issuing certs for IPA services
- **Web UI** — at `https://domain-controller.sweet.home/ipa/ui/`
## First-time use
```bash
# SSH to the server
ssh wayne@domain-controller
# Get a Kerberos ticket as admin
kinit admin
# List IPA users
ipa user-find
# Add a user
ipa user-add jdoe --first=John --last=Doe --password
# Check service health
ipactl status
```
## Credentials
The `admin` Kerberos password and Directory Manager password were
generated at install time. They are **not stored in this repo** — keep
them in your password manager.
- **admin** — used for day-to-day IPA management (`kinit admin`)
- **Directory Manager** — low-level LDAP root, rarely needed
To reset the admin password (requires being logged in as admin):
```bash
kinit admin
ipa passwd admin
```
## DNS integration with Pi-hole
Pi-hole must forward `sweet.home` DNS queries to this server so
that Kerberos SRV records and IPA host records resolve correctly.
Run `scripts/configure-pihole-dns.sh` once, or add the following
to Pi-hole's custom DNS config manually:
```
server=/sweet.home/192.168.2.138
```
See `docs/pihole-dns.md` for the full setup.
## Ports required (firewalld)
FreeIPA's firewalld config is applied by `ipa-server-install` automatically.
The following ports must be reachable from LAN clients:
| Port | Proto | Service |
|------|-------|---------|
| 80 | TCP | HTTP (redirect to HTTPS) |
| 443 | TCP | HTTPS / Web UI |
| 389 | TCP | LDAP |
| 636 | TCP | LDAPS |
| 88 | TCP+UDP | Kerberos |
| 464 | TCP+UDP | Kerberos password change |
| 53 | TCP+UDP | DNS |
## Reproducing this setup
See `docs/install.md` for the full step-by-step install procedure,
or run `scripts/install.sh` on a fresh Rocky Linux 9 VM with the
correct hostname and IP already set.
## Backup
The CA certificates (required for replicas) are at `/root/cacert.p12`
on the server, encrypted with the Directory Manager password.
Back these up to a secure location.
+248
View File
@@ -0,0 +1,248 @@
# FreeIPA Install Procedure
Full reproduction guide for `domain-controller.sweet.home`. Tested on
Rocky Linux 9.8 (GenericCloud), VMID 108, `pve1.sweet.home`.
## Prerequisites
- Proxmox node with `local-zfs` storage and internet access from guests
- Rocky Linux 9 GenericCloud image downloaded (see step 1)
- SSH access to Proxmox node as a user with `sudo` for `qm`/`pvesm`
- The operator's SSH public key available to inject via cloud-init
---
## Step 1 — Download Rocky Linux 9 GenericCloud image
On the Proxmox node, download to your ISO/image store:
```bash
wget -O /mnt/pve/server-iso/Rocky-9-GenericCloud.latest.x86_64.qcow2 \
https://download.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2
```
The image is ~617 MB. The Proxmox storage must be configured to accept
both ISO images and disk images (set "Content" to include "Disk image"
in the Proxmox UI for that storage).
---
## Step 2 — Prepare the VM in Proxmox
Use an existing VM or create a new one. The config used for VMID 108:
- **CPU**: 2 cores, x86-64-v2-AES
- **RAM**: 2048 MB
- **Disk**: 32 GB on `local-zfs`
- **BIOS**: SeaBIOS (GenericCloud uses MBR — not UEFI)
- **Network**: virtio on `vmbr0`, firewall enabled
- **QEMU guest agent**: enabled
If rebuilding an existing VM (e.g. replacing a prior OS):
```bash
# On pve1 — stop the VM
sudo qm stop <VMID>
# Remove existing disks from config
sudo qm set <VMID> --delete scsi0,efidisk0
# Switch to SeaBIOS if the VM was UEFI
sudo qm set <VMID> --bios seabios
# Free old disk volumes from storage
sudo pvesm free local-zfs:vm-<VMID>-disk-0
sudo pvesm free local-zfs:vm-<VMID>-disk-1
```
### Import the Rocky image and configure cloud-init
```bash
# Import image as a new disk
sudo qm importdisk <VMID> /mnt/pve/server-iso/Rocky-9-GenericCloud.latest.x86_64.qcow2 local-zfs
# Check what disk name was assigned
sudo qm config <VMID> # look for unused0: local-zfs:vm-<VMID>-disk-N
# Attach as scsi0 (adjust disk name from above)
sudo qm set <VMID> --scsi0 local-zfs:vm-<VMID>-disk-0,iothread=1
# Resize to 32 GB
sudo qm disk resize <VMID> scsi0 32G
# Add cloud-init drive
sudo qm set <VMID> --ide2 local-zfs:cloudinit
# Write SSH public key to a temp file
echo 'ssh-rsa AAAA... wayne@stream' > /tmp/admin-key.pub
# (use the key from variables.nix adminSshKey)
# Configure cloud-init
sudo qm set <VMID> \
--ciuser wayne \
--sshkeys /tmp/admin-key.pub \
--ipconfig0 ip=dhcp \
--nameserver 192.168.2.253 \
--searchdomain sweet.home
# Set boot order
sudo qm set <VMID> --boot order=scsi0
# Start VM
sudo qm start <VMID>
```
### Note on SSH key mismatch
The GenericCloud image injects the cloud-init SSH key on first boot.
If you need to add an additional key (e.g. from a different machine)
after first boot, mount the disk via nbd while the VM is stopped:
```bash
sudo qm stop <VMID>
sudo qemu-nbd --connect=/dev/nbd1 --format=raw /dev/zvol/rpool/data/vm-<VMID>-disk-0
# wait 2s, then:
sudo mount /dev/nbd1p4 /mnt/vm # p4 is the root partition on Rocky 9 GenericCloud
sudo tee -a /mnt/vm/home/wayne/.ssh/authorized_keys <<< 'ssh-ed25519 AAAA... extra-key'
sudo umount /mnt/vm
sudo qemu-nbd --disconnect /dev/nbd1
sudo qm start <VMID>
```
Rocky 9 GenericCloud partition layout: `p1`=BIOS boot (2M),
`p2`=EFI (100M), `p3`=/boot (1G), `p4`=/ (rest).
---
## Step 3 — First-boot system preparation
SSH in as `wayne` once cloud-init has completed (usually 6090 s):
```bash
ssh wayne@<VM-IP>
```
### Add swap (required — FreeIPA needs headroom beyond 1.7 GB RAM)
```bash
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
```
### Set static IP via NetworkManager
```bash
CON=$(nmcli -t -f NAME con show --active | head -1)
sudo nmcli con mod "$CON" \
ipv4.method manual \
ipv4.addresses 192.168.2.138/24 \
ipv4.gateway 192.168.2.254 \
ipv4.dns 192.168.2.253 \
ipv4.dns-search sweet.home
sudo nmcli con up "$CON"
```
### Fix /etc/hosts (cloud-init maps FQDN to 127.0.0.1 — IPA requires real IP)
```bash
sudo sed -i '/domain-controller/d' /etc/hosts
echo '192.168.2.138 domain-controller.sweet.home domain-controller' \
| sudo tee -a /etc/hosts
# Prevent cloud-init from resetting this on reboot
sudo sed -i 's/manage_etc_hosts: true/manage_etc_hosts: false/' \
/etc/cloud/cloud.cfg
```
---
## Step 4 — Install FreeIPA packages
```bash
sudo dnf install -y ipa-server ipa-server-dns
```
This pulls ~200 packages including 389-ds, Dogtag PKI, BIND, and MIT
Kerberos. Takes 510 minutes depending on mirror speed.
---
## Step 5 — Run the unattended install
Generate strong passwords (min 8 chars; store them in your password manager):
```bash
DM_PASS=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24)
ADMIN_PASS=$(openssl rand -base64 24 | tr -dc 'A-Za-z0-9' | head -c 24)
echo "Directory Manager: $DM_PASS"
echo "IPA Admin: $ADMIN_PASS"
# Save both in your password manager NOW before proceeding
```
Run the installer (takes 1520 minutes):
```bash
sudo ipa-server-install \
--realm=SWEET.HOME \
--domain=sweet.home \
--hostname=domain-controller.sweet.home \
--ds-password="$DM_PASS" \
--admin-password="$ADMIN_PASS" \
--setup-dns \
--forwarder=192.168.2.253 \
--no-dnssec-validation \
--no-ntp \
--unattended
```
Key flags:
- `--setup-dns` — install BIND as IPA's authoritative DNS for `sweet.home`
- `--forwarder=192.168.2.253` — forward non-sweet.home queries to Pi-hole
- `--no-dnssec-validation` — skip DNSSEC (home lab has no DNSSEC chain)
- `--no-ntp` — Proxmox handles time sync for guests; don't install chrony
---
## Step 6 — Verify
```bash
# All services should show RUNNING
ipactl status
# Get a Kerberos ticket and confirm
echo "$ADMIN_PASS" | kinit admin
klist
# Check DNS SRV records are in place
dig +short _kerberos._udp.sweet.home SRV @127.0.0.1
# Expected: 0 100 88 domain-controller.sweet.home.
```
---
## Step 7 — Configure Pi-hole to forward sweet.home DNS
See `docs/pihole-dns.md` and `scripts/configure-pihole-dns.sh`.
The short version — add to Pi-hole's custom dnsmasq config:
```
server=/sweet.home/192.168.2.138
```
---
## Step 8 — Back up the CA certificate
```bash
# On domain-controller (encrypted with Directory Manager password)
ls -lh /root/cacert.p12
# Copy to a safe location
scp root@domain-controller:/root/cacert.p12 ~/backups/ipa-cacert.p12
```
This file is required if you ever set up a replica or need to
re-issue service certificates.
+70
View File
@@ -0,0 +1,70 @@
# Pi-hole DNS forwarding for sweet.home
FreeIPA's BIND instance is the authoritative DNS server for the
`sweet.home` zone. Pi-hole (at `192.168.2.253`) is the LAN resolver
for everything else. To make Kerberos, LDAP, and host lookups work
from all LAN clients, Pi-hole must forward `sweet.home` queries to
the domain-controller.
## What needs configuring
One dnsmasq directive on Pi-hole:
```
server=/sweet.home/192.168.2.138
```
This makes Pi-hole forward every `*.sweet.home` query (and the bare
`sweet.home` zone) to `192.168.2.138` (domain-controller) instead
of its upstream resolver.
## How to apply
Use `scripts/configure-pihole-dns.sh` for an automated, idempotent
application, or apply manually as below.
### Manual — Pi-hole v6 (TOML config)
Pi-hole v6 reads custom dnsmasq options from:
```
/etc/pihole/pihole.toml → dns.customDnsEntries
```
or from drop-in files in `/etc/dnsmasq.d/`. The drop-in approach
is more robust across upgrades:
```bash
ssh <pihole-host>
echo 'server=/sweet.home/192.168.2.138' \
| sudo tee /etc/dnsmasq.d/10-ipa-sweet-home.conf
sudo pihole restartdns
```
Verify:
```bash
dig +short _kerberos._udp.sweet.home SRV @192.168.2.253
# Should return: 0 100 88 domain-controller.sweet.home.
dig +short domain-controller.sweet.home A @192.168.2.253
# Should return: 192.168.2.138
```
## Why this is required
FreeIPA clients use DNS SRV records to discover the KDC, LDAP server,
and other IPA services. Without the forwarder, Pi-hole returns NXDOMAIN
for `_kerberos._udp.sweet.home` and `_ldap._tcp.sweet.home`, which
causes `kinit` and IPA client enrollment to fail — even if the IP
address is reachable.
## Records IPA publishes
Once forwarding is set up, Pi-hole clients will be able to resolve:
| Record | Type | Value |
|--------|------|-------|
| `domain-controller.sweet.home` | A | `192.168.2.138` |
| `_kerberos._udp.sweet.home` | SRV | `0 100 88 domain-controller.sweet.home` |
| `_kerberos._tcp.sweet.home` | SRV | `0 100 88 domain-controller.sweet.home` |
| `_ldap._tcp.sweet.home` | SRV | `0 100 389 domain-controller.sweet.home` |
| `_kpasswd._udp.sweet.home` | SRV | `0 100 464 domain-controller.sweet.home` |
| `_kerberos.sweet.home` | TXT | `"SWEET.HOME"` |
| `ipa-ca.sweet.home` | A | `192.168.2.138` |
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Configure Pi-hole to forward sweet.home DNS queries to FreeIPA.
#
# Usage:
# bash configure-pihole-dns.sh <pihole-host>
# bash configure-pihole-dns.sh pihole.sweet.home
#
# Idempotent — safe to run multiple times.
# Requires SSH access to the Pi-hole host as a user with sudo.
set -euo pipefail
PIHOLE_HOST="${1:-}"
IPA_IP="192.168.2.138"
IPA_DOMAIN="sweet.home"
DNSMASQ_CONF="/etc/dnsmasq.d/10-ipa-${IPA_DOMAIN//./-}.conf"
DIRECTIVE="server=/${IPA_DOMAIN}/${IPA_IP}"
if [[ -z "$PIHOLE_HOST" ]]; then
echo "Usage: $0 <pihole-host>" >&2
exit 1
fi
echo "==> Configuring Pi-hole at $PIHOLE_HOST to forward $IPA_DOMAIN$IPA_IP..."
ssh "$PIHOLE_HOST" "
set -euo pipefail
if grep -qF '${DIRECTIVE}' '${DNSMASQ_CONF}' 2>/dev/null; then
echo '==> Forwarder already configured, skipping write.'
else
echo '${DIRECTIVE}' | sudo tee '${DNSMASQ_CONF}'
echo '==> Written to ${DNSMASQ_CONF}'
fi
echo '==> Restarting Pi-hole DNS...'
sudo pihole restartdns
echo '==> Verifying SRV record resolution via Pi-hole...'
sleep 2
dig +short _kerberos._udp.${IPA_DOMAIN} SRV @127.0.0.1 || true
"
echo "==> Done. Pi-hole now forwards ${IPA_DOMAIN} queries to ${IPA_IP}."
echo " Verify from a LAN client:"
echo " dig +short _kerberos._udp.${IPA_DOMAIN} SRV @${PIHOLE_HOST}"
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# FreeIPA server install script for domain-controller.sweet.home
#
# Run this on a fresh Rocky Linux 9 VM that already has:
# - Correct hostname: domain-controller.sweet.home
# - Static IP: 192.168.2.138/24
# - Gateway: 192.168.2.254
# - DNS: 192.168.2.253 (Pi-hole)
# - sudo access for the current user
#
# The script will prompt for passwords if not set via environment:
# IPA_DM_PASSWORD Directory Manager password (store in password manager)
# IPA_ADMIN_PASSWORD IPA admin Kerberos password (store in password manager)
#
# Full procedure: see docs/install.md
set -euo pipefail
IPA_REALM="SWEET.HOME"
IPA_DOMAIN="sweet.home"
IPA_HOSTNAME="domain-controller.sweet.home"
IPA_IP="192.168.2.138"
IPA_DNS_FORWARDER="192.168.2.253"
# ── Password handling ────────────────────────────────────────────────────────
if [[ -z "${IPA_DM_PASSWORD:-}" ]]; then
read -r -s -p "Directory Manager password (min 8 chars): " IPA_DM_PASSWORD
echo
fi
if [[ -z "${IPA_ADMIN_PASSWORD:-}" ]]; then
read -r -s -p "IPA admin password (min 8 chars): " IPA_ADMIN_PASSWORD
echo
fi
if [[ ${#IPA_DM_PASSWORD} -lt 8 || ${#IPA_ADMIN_PASSWORD} -lt 8 ]]; then
echo "ERROR: passwords must be at least 8 characters" >&2
exit 1
fi
# ── Pre-flight checks ────────────────────────────────────────────────────────
echo "==> Checking hostname..."
actual_fqdn=$(hostname -f)
if [[ "$actual_fqdn" != "$IPA_HOSTNAME" ]]; then
echo "ERROR: hostname -f returned '$actual_fqdn', expected '$IPA_HOSTNAME'" >&2
exit 1
fi
echo "==> Checking /etc/hosts entry..."
if ! grep -q "$IPA_IP $IPA_HOSTNAME" /etc/hosts; then
echo "ERROR: /etc/hosts does not have '$IPA_IP $IPA_HOSTNAME'" >&2
echo "Fix: sudo sed -i '/$IPA_HOSTNAME/d' /etc/hosts && echo '$IPA_IP $IPA_HOSTNAME ${IPA_HOSTNAME%%.*}' | sudo tee -a /etc/hosts" >&2
exit 1
fi
echo "==> Checking Python FQDN resolution..."
resolved=$(python3 -c "import socket; print(socket.gethostbyname('$IPA_HOSTNAME'))" 2>/dev/null || true)
if [[ "$resolved" != "$IPA_IP" ]]; then
echo "ERROR: $IPA_HOSTNAME resolves to '$resolved', expected '$IPA_IP'" >&2
exit 1
fi
echo "==> Checking internet connectivity..."
if ! ping -c1 -W5 8.8.8.8 >/dev/null 2>&1; then
echo "ERROR: no internet connectivity (needed for package install)" >&2
exit 1
fi
# ── Swap ─────────────────────────────────────────────────────────────────────
if ! swapon --show | grep -q .; then
echo "==> Creating 2 GB swap file (FreeIPA needs headroom)..."
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap defaults 0 0' | sudo tee -a /etc/fstab
else
echo "==> Swap already configured, skipping."
fi
# ── Packages ─────────────────────────────────────────────────────────────────
echo "==> Installing FreeIPA server packages..."
sudo dnf install -y ipa-server ipa-server-dns
# ── Install ──────────────────────────────────────────────────────────────────
echo "==> Running ipa-server-install (1520 min)..."
sudo ipa-server-install \
--realm="$IPA_REALM" \
--domain="$IPA_DOMAIN" \
--hostname="$IPA_HOSTNAME" \
--ds-password="$IPA_DM_PASSWORD" \
--admin-password="$IPA_ADMIN_PASSWORD" \
--setup-dns \
--forwarder="$IPA_DNS_FORWARDER" \
--no-dnssec-validation \
--no-ntp \
--unattended
# ── Verify ───────────────────────────────────────────────────────────────────
echo "==> Verifying services..."
sudo ipactl status
echo "==> Verifying Kerberos ticket..."
echo "$IPA_ADMIN_PASSWORD" | kinit admin
klist
echo "==> Verifying DNS SRV records..."
dig +short _kerberos._udp."$IPA_DOMAIN" SRV @127.0.0.1
echo ""
echo "======================================================================"
echo "Setup complete. Next steps:"
echo ""
echo " 1. Save passwords to your password manager (if not already done)."
echo ""
echo " 2. Configure Pi-hole to forward $IPA_DOMAIN DNS to $IPA_IP:"
echo " bash scripts/configure-pihole-dns.sh <pihole-host>"
echo ""
echo " 3. Back up the CA certificates:"
echo " scp root@$IPA_HOSTNAME:/root/cacert.p12 ~/backups/ipa-cacert.p12"
echo " (Encrypted with the Directory Manager password)"
echo ""
echo " 4. Access the Web UI at:"
echo " https://$IPA_HOSTNAME/ipa/ui/"
echo "======================================================================"
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Read-only health check for the FreeIPA server.
# Run locally on domain-controller or remotely:
# ssh wayne@domain-controller 'bash -s' < scripts/verify.sh
#
# Exit code 0 = all checks passed, non-zero = something is wrong.
set -uo pipefail
PASS=0
FAIL=0
check() {
local label="$1"
shift
if "$@" >/dev/null 2>&1; then
echo " OK $label"
(( PASS++ )) || true
else
echo "FAIL $label"
(( FAIL++ )) || true
fi
}
echo "=== FreeIPA health check: $(hostname -f) ==="
echo ""
echo "--- Services ---"
check "ipactl status" sudo ipactl status
check "dirsrv running" systemctl is-active dirsrv.target
check "krb5kdc running" systemctl is-active krb5kdc
check "named running" systemctl is-active named
check "httpd running" systemctl is-active httpd
check "pki-tomcatd running" systemctl is-active pki-tomcatd.target
echo ""
echo "--- DNS ---"
check "A record: domain-controller.sweet.home" dig +short domain-controller.sweet.home A @127.0.0.1
check "SRV: _kerberos._udp.sweet.home" dig +short _kerberos._udp.sweet.home SRV @127.0.0.1
check "SRV: _ldap._tcp.sweet.home" dig +short _ldap._tcp.sweet.home SRV @127.0.0.1
check "TXT: _kerberos.sweet.home" dig +short _kerberos.sweet.home TXT @127.0.0.1
echo ""
echo "--- LDAP ---"
check "LDAP port 389 open" bash -c "exec 3<>/dev/tcp/127.0.0.1/389"
check "LDAPS port 636 open" bash -c "exec 3<>/dev/tcp/127.0.0.1/636"
echo ""
echo "--- Kerberos ---"
check "KDC port 88 open" bash -c "exec 3<>/dev/tcp/127.0.0.1/88"
echo ""
echo "--- HTTP ---"
check "IPA HTTP redirect" curl -sk -o /dev/null -w "%{http_code}" http://localhost/ | grep -qE "^(301|302|200)"
check "IPA HTTPS UI" curl -sk -o /dev/null -w "%{http_code}" https://localhost/ipa/ui/ | grep -q "200"
echo ""
if [[ $FAIL -eq 0 ]]; then
echo "All $PASS checks passed."
else
echo "$FAIL check(s) FAILED, $PASS passed."
exit 1
fi