|
|
|
@@ -38,47 +38,113 @@ lib.mkIf enabled {
|
|
|
|
|
networking.domain = lib.mkDefault vars.homeDomain;
|
|
|
|
|
networking.nameservers = lib.mkDefault [ vars.domainControllerIp ];
|
|
|
|
|
|
|
|
|
|
security.ipa = {
|
|
|
|
|
enable = true;
|
|
|
|
|
domain = vars.homeDomain;
|
|
|
|
|
inherit realm;
|
|
|
|
|
server = vars.ipaServer;
|
|
|
|
|
certificate = caCertPkg;
|
|
|
|
|
inherit basedn;
|
|
|
|
|
ipaHostname = fqdn;
|
|
|
|
|
offlinePasswords = true;
|
|
|
|
|
cacheCredentials = true;
|
|
|
|
|
security = {
|
|
|
|
|
ipa = {
|
|
|
|
|
enable = true;
|
|
|
|
|
domain = vars.homeDomain;
|
|
|
|
|
inherit realm;
|
|
|
|
|
server = vars.ipaServer;
|
|
|
|
|
certificate = caCertPkg;
|
|
|
|
|
inherit basedn;
|
|
|
|
|
ipaHostname = fqdn;
|
|
|
|
|
offlinePasswords = true;
|
|
|
|
|
cacheCredentials = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
# lightdm also needs this so the GUI login path can create the home dir
|
|
|
|
|
# if it was not pre-seeded by the tmpfiles rule above (e.g. on first boot
|
|
|
|
|
# before SSSD has resolved the user).
|
|
|
|
|
pam.services = {
|
|
|
|
|
sshd.makeHomeDir = true;
|
|
|
|
|
lightdm.makeHomeDir = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# HM with useUserPackages = true (flake.nix) sets users.users.${ipaUser}.packages,
|
|
|
|
|
# which forces the stub into /etc/passwd. pam_sss.so with the "localusers" flag
|
|
|
|
|
# (added by NixOS when SSSD is enabled) then skips SSSD for any user it finds in
|
|
|
|
|
# local /etc/passwd — including this stub — falling through to pam_unix, which has
|
|
|
|
|
# no password for the stub → sudo auth always fails.
|
|
|
|
|
#
|
|
|
|
|
# Fix: NOPASSWD for the IPA user. The IPA user already authenticated to reach a
|
|
|
|
|
# shell (SSH public key from IPA or Kerberos), so re-prompting via a broken PAM
|
|
|
|
|
# path is security theater on a single-admin homelab.
|
|
|
|
|
sudo.extraRules = [{
|
|
|
|
|
users = [ vars.ipaUser ];
|
|
|
|
|
commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
|
|
|
|
|
}];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# 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"
|
|
|
|
|
# Pre-create the IPA user's home dir so Home Manager activation succeeds
|
|
|
|
|
# even before their first login. On a fresh system SSSD may not have
|
|
|
|
|
# resolved the user yet — tmpfiles warns and skips in that case (non-fatal),
|
|
|
|
|
# and pam_mkhomedir covers the first-login path as a fallback.
|
|
|
|
|
"d /home/${vars.ipaUser} 0700 ${vars.ipaUser} ${vars.ipaUser} - -"
|
|
|
|
|
];
|
|
|
|
|
systemd = {
|
|
|
|
|
# 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.
|
|
|
|
|
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"
|
|
|
|
|
# Pre-create the IPA user's home dir so Home Manager activation succeeds
|
|
|
|
|
# even before their first login. On a fresh system SSSD may not have
|
|
|
|
|
# resolved the user yet — tmpfiles warns and skips in that case (non-fatal),
|
|
|
|
|
# and pam_mkhomedir covers the first-login path as a fallback.
|
|
|
|
|
"d /home/${vars.ipaUser} 0700 ${vars.ipaUser} ${vars.ipaUser} - -"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
# security.ipa enables Kerberos (security.krb5) which causes systemd to
|
|
|
|
|
# start auth-rpcgss-module.service and rpc-gssd.service for Kerberos NFS
|
|
|
|
|
# authentication. LXC containers can't load the auth_rpcgss kernel module
|
|
|
|
|
# and don't have /var/lib/nfs/rpc_pipefs, so both services fail.
|
|
|
|
|
#
|
|
|
|
|
# The NixOS IPA module already adds a drop-in for auth-rpcgss-module.service
|
|
|
|
|
# with ConditionPathExists=/etc/krb5.keytab. We use lib.mkForce to win the
|
|
|
|
|
# text conflict and add ConditionVirtualization=!container alongside it so
|
|
|
|
|
# the service is skipped (not failed) in containers that do have a keytab.
|
|
|
|
|
# Same fix for rpc-gssd.service which also fails in containers.
|
|
|
|
|
units = lib.mkIf config.boot.isContainer {
|
|
|
|
|
"auth-rpcgss-module.service" = {
|
|
|
|
|
overrideStrategy = "asDropinIfExists";
|
|
|
|
|
text = lib.mkForce ''
|
|
|
|
|
[Unit]
|
|
|
|
|
ConditionPathExists=
|
|
|
|
|
ConditionPathExists=/etc/krb5.keytab
|
|
|
|
|
ConditionVirtualization=!container
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
# rpc-gssd also has ConditionPathExists from the NixOS IPA module (and an
|
|
|
|
|
# X-Restart-Triggers store path from systemd.nix). Use mkForce to win;
|
|
|
|
|
# omit X-Restart-Triggers since this service is skipped in containers anyway.
|
|
|
|
|
"rpc-gssd.service" = {
|
|
|
|
|
overrideStrategy = "asDropinIfExists";
|
|
|
|
|
text = lib.mkForce ''
|
|
|
|
|
[Unit]
|
|
|
|
|
ConditionPathExists=
|
|
|
|
|
ConditionPathExists=/etc/krb5.keytab
|
|
|
|
|
ConditionVirtualization=!container
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# home-manager-<user>.service fails on first enrollment because /home/wayne
|
|
|
|
|
# doesn't exist until the user's first login (pam_mkhomedir creates it then).
|
|
|
|
|
# ConditionPathExists makes systemd skip the service (exit 0, condition not
|
|
|
|
|
# met) instead of failing. After first login the dir exists and subsequent
|
|
|
|
|
# rebuilds activate HM normally.
|
|
|
|
|
services."home-manager-${vars.ipaUser}".unitConfig.ConditionPathExists =
|
|
|
|
|
"/home/${vars.ipaUser}";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
@@ -92,44 +158,6 @@ lib.mkIf enabled {
|
|
|
|
|
restartUnits = [ "sssd.service" ];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# security.ipa enables Kerberos (security.krb5) which causes systemd to
|
|
|
|
|
# start auth-rpcgss-module.service and rpc-gssd.service for Kerberos NFS
|
|
|
|
|
# authentication. LXC containers can't load the auth_rpcgss kernel module
|
|
|
|
|
# and don't have /var/lib/nfs/rpc_pipefs, so both services fail.
|
|
|
|
|
#
|
|
|
|
|
# The NixOS IPA module already adds a drop-in for auth-rpcgss-module.service
|
|
|
|
|
# with ConditionPathExists=/etc/krb5.keytab. We use lib.mkForce to win the
|
|
|
|
|
# text conflict and add ConditionVirtualization=!container alongside it so
|
|
|
|
|
# the service is skipped (not failed) in containers that do have a keytab.
|
|
|
|
|
# Same fix for rpc-gssd.service which also fails in containers.
|
|
|
|
|
systemd.units = lib.mkIf config.boot.isContainer {
|
|
|
|
|
"auth-rpcgss-module.service" = {
|
|
|
|
|
overrideStrategy = "asDropinIfExists";
|
|
|
|
|
text = lib.mkForce ''
|
|
|
|
|
[Unit]
|
|
|
|
|
ConditionPathExists=
|
|
|
|
|
ConditionPathExists=/etc/krb5.keytab
|
|
|
|
|
ConditionVirtualization=!container
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
# rpc-gssd also has ConditionPathExists from the NixOS IPA module (and an
|
|
|
|
|
# X-Restart-Triggers store path from systemd.nix). Use mkForce to win;
|
|
|
|
|
# omit X-Restart-Triggers since this service is skipped in containers anyway.
|
|
|
|
|
"rpc-gssd.service" = {
|
|
|
|
|
overrideStrategy = "asDropinIfExists";
|
|
|
|
|
text = lib.mkForce ''
|
|
|
|
|
[Unit]
|
|
|
|
|
ConditionPathExists=
|
|
|
|
|
ConditionPathExists=/etc/krb5.keytab
|
|
|
|
|
ConditionVirtualization=!container
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
# Home Manager config for the IPA primary user, applied on every enrolled
|
|
|
|
|
# host. Manages what IPA doesn't: dotfiles, user-scoped packages, session
|
|
|
|
|
# variables. Switch-nix/Test-nix/buildImage are system-wide (configuration.nix)
|
|
|
|
|
# so they don't need to be repeated here.
|
|
|
|
|
# NixOS requires isNormalUser/isSystemUser + group on any entry in
|
|
|
|
|
# users.users. HM with useUserPackages = true (set in flake.nix) adds a stub
|
|
|
|
|
# entry for each HM user so it can install packages to
|
|
|
|
@@ -140,6 +168,7 @@ lib.mkIf enabled {
|
|
|
|
|
users.users.${vars.ipaUser} = {
|
|
|
|
|
isNormalUser = true;
|
|
|
|
|
group = "users";
|
|
|
|
|
extraGroups = [ "wheel" ];
|
|
|
|
|
createHome = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|