Archived
Check NixOS configurations / eval-hosts (pull_request) Failing after 9m53s
Script fixes: - Rename HOSTNAME variable to TARGET (shadowed the bash builtin) - Fix ipa-getkeytab -s to always use IPA_SERVER, not DC_HOST (diverge if --dc is overridden to a jump host) - Remove dead REALM variable - Add EXIT trap to delete the plaintext keytab if the script aborts before sops encryption completes; cleared after successful encrypt - Distinguish real ipa host-add failures from "already exists" instead of swallowing all errors with || true - Warn explicitly when no platform age keys exist for the target (keytab would be admin-only and the host couldn't decrypt it at boot) - Fix sops fallback from pinned nixos-25.11 channel to nixpkgs (uses the repo's own flake.lock) - Expand "next steps" output to include networking.domain and nameservers lines that host.nix requires for IPA membership Module docs: - Point to the script as the primary setup path; move manual steps to a fallback section - Note that certs/ipa-ca.crt is already committed (no need to re-fetch) - Document the networking.domain and nameservers requirements in the header - Add sync-host-keys.sh as explicit step 0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
4.3 KiB
Nix
104 lines
4.3 KiB
Nix
# Fully declarative FreeIPA domain membership.
|
|
#
|
|
# Configures security.ipa (SSSD, Kerberos, PAM, NSSwitch) and places a
|
|
# pre-provisioned host keytab via sops-nix so no imperative ipa-client-install
|
|
# step is needed after deployment.
|
|
#
|
|
# Usage (in a host.nix imports list):
|
|
# (import ../../modules/ipa/client.nix {
|
|
# keytabSopsFile = ../../secrets/<hostname>.keytab;
|
|
# caCertFile = ../../certs/ipa-ca.crt; # already committed — do not re-fetch
|
|
# })
|
|
#
|
|
# The host.nix networking block must also set:
|
|
# networking.domain = vars.homeDomain; # needed for Kerberos FQDN
|
|
# networking.nameservers = [ vars.domainControllerIp ]; # IPA DNS
|
|
#
|
|
# One-time operator setup per host (do this BEFORE deploying):
|
|
#
|
|
# 0. Generate SSH host keys and the host's age key for sops:
|
|
# scripts/secrets/sync-host-keys.sh <flake-target>
|
|
# This must run before step 1 so the host age key is in .sops.yaml
|
|
# and the keytab can be encrypted for the host to read at boot.
|
|
#
|
|
# 1. Add the IPA host account and produce the sops-encrypted keytab:
|
|
# scripts/ipa/create-nixos-ipa-host-account.sh [--ip <addr>] <hostname>
|
|
# The script handles ipa host-add, ipa-getkeytab, .sops.yaml patching,
|
|
# and sops encryption in one step. See the script header for details.
|
|
#
|
|
# 2. Wire up the host (see "Usage" above), then deploy:
|
|
# nixos-rebuild switch (or create-proxmox-resource.sh)
|
|
# No further manual enrollment steps are required after deployment.
|
|
#
|
|
# Manual fallback (if the script isn't usable):
|
|
# a. On the FreeIPA server: ipa host-add <fqdn> [--ip-address=<ip>] --force
|
|
# b. On the FreeIPA server: ipa-getkeytab -s <ipa-server> -p host/<fqdn> -k /tmp/<host>.keytab
|
|
# c. From the repo root (path must match for sops creation rule to apply):
|
|
# cp /tmp/<host>.keytab secrets/<host>.keytab
|
|
# sops -e --input-type binary -i secrets/<host>.keytab
|
|
# d. Commit secrets/<host>.keytab and the updated .sops.yaml, then deploy.
|
|
#
|
|
# vars dependencies: homeDomain, ipaServer, domainControllerIp
|
|
|
|
{ keytabSopsFile, caCertFile }:
|
|
{ config, lib, pkgs, vars, ... }:
|
|
|
|
let
|
|
realm = lib.strings.toUpper vars.homeDomain;
|
|
fqdn = "${config.networking.hostName}.${vars.homeDomain}";
|
|
# "sweet.home" -> "dc=sweet,dc=home"
|
|
basedn = lib.strings.concatMapStringsSep "," (c: "dc=${c}") (lib.strings.splitString "." vars.homeDomain);
|
|
# security.ipa.certificate expects a derivation (package), not a raw path.
|
|
caCertPkg = pkgs.writeText "ipa-ca.crt" (builtins.readFile caCertFile);
|
|
in
|
|
{
|
|
security.ipa = {
|
|
enable = true;
|
|
domain = vars.homeDomain;
|
|
realm = realm;
|
|
server = vars.ipaServer;
|
|
certificate = caCertPkg;
|
|
basedn = basedn;
|
|
ipaHostname = fqdn;
|
|
offlinePasswords = true;
|
|
cacheCredentials = true;
|
|
};
|
|
|
|
# Fetch SSH public keys from IPA so users can log in with the key stored
|
|
# in their IPA profile rather than needing ~/.ssh/authorized_keys on every
|
|
# host. sss_ssh_authorizedkeys queries SSSD (which queries IPA LDAP).
|
|
#
|
|
# /nix/store is 1775 (group-writable by nixbld). OpenSSH 10.0+ rejects
|
|
# AuthorizedKeysCommand binaries whose path contains any group-writable
|
|
# component, silently skipping the command. Copy to /usr/local/bin (all
|
|
# components root-owned, 755) so the path passes sshd's safety check.
|
|
systemd.tmpfiles.rules = [
|
|
"d /usr/local 0755 root root - -"
|
|
"d /usr/local/bin 0755 root root - -"
|
|
"C+ /usr/local/bin/sss_ssh_authorizedkeys 0555 root root - ${pkgs.sssd}/bin/sss_ssh_authorizedkeys"
|
|
];
|
|
|
|
services.openssh.extraConfig = ''
|
|
AuthorizedKeysCommand /usr/local/bin/sss_ssh_authorizedkeys %u
|
|
AuthorizedKeysCommandUser nobody
|
|
'';
|
|
|
|
# Create the home directory on first login if it doesn't exist yet.
|
|
# IPA users have no pre-created home on the host; without this sshd
|
|
# opens a session to a non-existent directory and resets the connection.
|
|
security.pam.services.sshd.makeHomeDir = true;
|
|
|
|
# Host keytab: pre-provisioned on the IPA server, sops-encrypted binary.
|
|
# Placed at /etc/krb5.keytab before SSSD starts so the host authenticates
|
|
# to IPA without running ipa-client-install.
|
|
sops.secrets."ipa-host-keytab" = {
|
|
sopsFile = keytabSopsFile;
|
|
format = "binary";
|
|
path = "/etc/krb5.keytab";
|
|
owner = "root";
|
|
group = "root";
|
|
mode = "0600";
|
|
restartUnits = [ "sssd.service" ];
|
|
};
|
|
}
|