98dc2917e8
Auf Basis des Grilling-Sessions mit 20 design decisions umgesetztes v1-Skeleton der Thin-Client-Rolle plus 3 Pilot-Hosts. Architektur: - roles/thin-client/ als geschlossene Rolle (default.nix compose + 7 Subdirectories: hardware, session, identity, network, peripherals, apps, monitoring, deployment) - hosts/AZ-TC-NN/default.nix als ~20-Zeilen-Wrapper pro Fleet-Host - flake.nix instanziiert AZ-TC-01..03 via Fleet-Helper - secrets.nix mit per-host agenix-Secret-Stubs Submodule: - hardware: dell-optiplex-micro + generic-x86_64-uefi Fallback - identity: AD (sssd/krb5/keytab via agenix), lokale Notfalluser (sascha.koenig + jannik.mueller ohne m3ta-home), sudo-Policy - session: KDE Plasma 6 + Wayland + SDDM, Branding (Wallpaper + Footer), PipeWire Audio - network: NetworkManager + wpa_supplicant + 802.1X EAP-TLS, NetBird + SSH via NetBird, systemd-resolved (Corp + NetBird DNS), hardened firewall, OpenSSH - peripherals: CUPS mit Pull-Print-Queue, pam_mount für DFS-Shares - apps: Chromium (ManagedBookmarks, Bitwarden force-install, no local passwords), Office-Web .desktop-Shortcuts, Remmina (mehrere TS, Kerberos SSO), RustDesk Client + Daemon, OBS Studio, Autostart - monitoring: node_exporter → Pushgateway, Alloy (stub für Loki), Snipe-IT Asset-Checkin (stub) - deployment: Disko BTRFS-Layout, auto-upgrade daily + reboot window, snapper snapshots Build-Validierung: 'nix flake check' bestanden für AZ-TC-01/02/03. Siehe roles/thin-client/README.md für den Provisionierungs-Workflow und die Liste der noch auszufüllenden Platzhalter (TODO-Kommentare in den jeweiligen Modulen).
168 lines
5.1 KiB
Nix
168 lines
5.1 KiB
Nix
# roles/thin-client/identity/ad.nix
|
|
#
|
|
# Active Directory integration for Thin Clients.
|
|
#
|
|
# Real realm: AZ-GROUP
|
|
# DNS domain: az-group.local
|
|
# Join mechanism: Pre-created computer accounts + keytab via agenix
|
|
# (no interactive realm join, no service account with join privileges).
|
|
#
|
|
# Provisioning per host (admin-side, one-shot):
|
|
# 1. adcli precreate --computer-name=AZ-TC-01$ \
|
|
# --domain=az-group.local \
|
|
# --host-fqdn=AZ-TC-01.az-group.local \
|
|
# --login-type=computer \
|
|
# --os-name="NixOS" --os-version="25.11" \
|
|
# --domain-ou="OU=ThinClients,DC=az-group,DC=local" \
|
|
# <admin>@AZ-GROUP
|
|
# 2. adcli join --computer-name=AZ-TC-01$ \
|
|
# --domain=az-group.local \
|
|
# --host-fqdn=AZ-TC-01.az-group.local \
|
|
# --login-type=computer \
|
|
# --user=<admin> --verbose \
|
|
# -K /tmp/AZ-TC-01.keytab
|
|
# 3. agenix -e secrets/AZ-TC-01-krb5-keytab.age (paste /tmp/AZ-TC-01.keytab)
|
|
# 4. rm /tmp/AZ-TC-01.keytab
|
|
# 5. Deploy; the keytab decrypts to /etc/krb5.keytab on the host.
|
|
#
|
|
# SSSD resolves users/groups via AD; kerberos auth via keytab for the
|
|
# machine account. User login uses their AD password (offline-capable via
|
|
# cache_credentials=true).
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
cfg = config.az.tc;
|
|
hostname = config.networking.hostName;
|
|
domain = "az-group.local";
|
|
realm = "AZ-GROUP";
|
|
in {
|
|
options.az.tc.ad = {
|
|
ou = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "OU=ThinClients,DC=az-group,DC=local";
|
|
description = ''
|
|
AD Organizational Unit where Thin Client computer objects live.
|
|
Override if your AD layout differs. Used for documentation and
|
|
the pre-create workflow; not consumed at runtime.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
environment.systemPackages = with pkgs; [
|
|
adcli
|
|
sssd
|
|
samba4Full
|
|
krb5
|
|
realmd
|
|
oddjob
|
|
];
|
|
|
|
# Kerberos client
|
|
security.krb5 = {
|
|
enable = true;
|
|
settings = {
|
|
libdefaults = {
|
|
default_realm = realm;
|
|
udp_preference_limit = 0;
|
|
forwardable = true;
|
|
proxiable = true;
|
|
rdns = false;
|
|
};
|
|
domain_realm = {
|
|
".az-group.local" = realm;
|
|
"az-group.local" = realm;
|
|
};
|
|
realms = {
|
|
"${realm}" = {
|
|
kdc = ["az-dc01.az-group.local" "az-dc02.az-group.local"];
|
|
admin_server = "az-dc01.az-group.local";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
# PAM: create per-user /home on first login (used by domain users)
|
|
security.pam = {
|
|
services.login.makeHomeDir = true;
|
|
services.sddm.makeHomeDir = true;
|
|
services.sshd.makeHomeDir = true;
|
|
makeHomeDir.umask = "077";
|
|
};
|
|
|
|
services.nscd.enable = true;
|
|
|
|
services.sssd = {
|
|
enable = true;
|
|
config = ''
|
|
[sssd]
|
|
domains = az-group.local
|
|
config_file_version = 2
|
|
services = nss, pam
|
|
|
|
[domain/az-group.local]
|
|
id_provider = ad
|
|
auth_provider = ad
|
|
access_provider = ad
|
|
chpass_provider = ad
|
|
ad_domain = ${domain}
|
|
ad_server = az-dc01.az-group.local, az-dc02.az-group.local
|
|
krb5_realm = ${realm}
|
|
krb5_server = az-dc01.az-group.local, az-dc02.az-group.local
|
|
krb5_keytab = /etc/krb5.keytab
|
|
krb5_store_password_if_offline = True
|
|
cache_credentials = True
|
|
use_fully_qualified_names = false
|
|
fallback_homedir = /home/%u
|
|
override_shell = /run/current-system/sw/bin/bash
|
|
default_shell = /run/current-system/sw/bin/bash
|
|
ad_gpo_access_control = permissive
|
|
enumerate = true
|
|
ldap_id_mapping = false
|
|
'';
|
|
};
|
|
|
|
# DNS — Thin Clients use AD DCs as resolver (NetBird DNS overlays
|
|
# for corp-VPN-only domains via systemd-resolved; see network/dns.nix)
|
|
networking.nameservers = lib.mkDefault ["az-dc01.az-group.local" "az-dc02.az-group.local"];
|
|
networking.domain = lib.mkDefault domain;
|
|
networking.search = lib.mkDefault [domain];
|
|
|
|
# Host FQDN — important for Kerberos SPN matching.
|
|
# `networking.hostName` is set by the host's default.nix; here we
|
|
# only add the hosts file fallback so the FQDN resolves locally even
|
|
# before DNS is reachable.
|
|
networking.extraHosts = ''
|
|
127.0.0.1 ${hostname}.${domain} ${hostname}
|
|
'';
|
|
|
|
# agenix-deployed machine keytab
|
|
age.secrets."${hostname}-krb5-keytab" = {
|
|
file = ../../secrets/${hostname}-krb5-keytab.age;
|
|
path = "/etc/krb5.keytab";
|
|
mode = "0600";
|
|
owner = "root";
|
|
group = "root";
|
|
};
|
|
|
|
# Ensure keytab path is readable by sssd (runs as root) but not by
|
|
# anyone else.
|
|
systemd.services.sssd = {
|
|
after = ["age-identity.service"];
|
|
wants = ["age-identity.service"];
|
|
serviceConfig.ExecStartPre = let
|
|
check = pkgs.writeShellScript "check-keytab" ''
|
|
if [ ! -s /etc/krb5.keytab ]; then
|
|
echo "ERROR: /etc/krb5.keytab is empty or missing." >&2
|
|
echo "Provision via adcli join + agenix, see roles/thin-client/README.md." >&2
|
|
exit 1
|
|
fi
|
|
'';
|
|
in ["+${check}"];
|
|
};
|
|
};
|
|
}
|