Archived
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5beed2d75c | ||
|
|
fe7fc55c04 | ||
|
|
6cdae391f4 | ||
|
|
95eb494370 | ||
|
|
5c2d61d35a | ||
|
|
e6f15404f5 | ||
|
|
1fc6e63178 | ||
|
|
232d0e7c40 |
@@ -14,6 +14,11 @@
|
|||||||
};
|
};
|
||||||
boot.zfs.forceImportRoot = false;
|
boot.zfs.forceImportRoot = false;
|
||||||
|
|
||||||
|
# Only advertise the LAN interface to IPA DNS. Without this, SSSD registers
|
||||||
|
# every Docker bridge (172.x.x.x) as an A record for docker.sweet.home —
|
||||||
|
# the default dyndns.interface = "*" catches them all.
|
||||||
|
security.ipa.dyndns.interface = vars.lxcLanInterface; # eth0
|
||||||
|
|
||||||
# Preserved from the pre-refactor `docker` target — stateVersion must never
|
# Preserved from the pre-refactor `docker` target — stateVersion must never
|
||||||
# be bumped on an already-installed machine.
|
# be bumped on an already-installed machine.
|
||||||
system.stateVersion = "25.05";
|
system.stateVersion = "25.05";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ pkgs, vars, ... }:
|
{ lib, pkgs, vars, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
# virtualisation.docker.enable = true;
|
# virtualisation.docker.enable = true;
|
||||||
@@ -15,6 +15,12 @@
|
|||||||
# experimental = true;
|
# experimental = true;
|
||||||
# };
|
# };
|
||||||
};
|
};
|
||||||
|
# Pin the docker group GID to match the IPA "docker-access" group so that
|
||||||
|
# IPA group membership alone grants access to the Docker socket. Any user
|
||||||
|
# whose supplementary groups (resolved by SSSD from IPA) include GID
|
||||||
|
# vars.dockerAccessGid will pass the socket group-permission check without
|
||||||
|
# any per-host users.groups.docker.members entry.
|
||||||
|
users.groups.docker.gid = lib.mkForce vars.dockerAccessGid;
|
||||||
users.users.${vars.primaryUser}.extraGroups = [ "docker" ];
|
users.users.${vars.primaryUser}.extraGroups = [ "docker" ];
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
docker-compose
|
docker-compose
|
||||||
|
|||||||
+58
-52
@@ -38,7 +38,8 @@ lib.mkIf enabled {
|
|||||||
networking.domain = lib.mkDefault vars.homeDomain;
|
networking.domain = lib.mkDefault vars.homeDomain;
|
||||||
networking.nameservers = lib.mkDefault [ vars.domainControllerIp ];
|
networking.nameservers = lib.mkDefault [ vars.domainControllerIp ];
|
||||||
|
|
||||||
security.ipa = {
|
security = {
|
||||||
|
ipa = {
|
||||||
enable = true;
|
enable = true;
|
||||||
domain = vars.homeDomain;
|
domain = vars.homeDomain;
|
||||||
inherit realm;
|
inherit realm;
|
||||||
@@ -50,6 +51,33 @@ lib.mkIf enabled {
|
|||||||
cacheCredentials = 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" ]; }];
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
# Fetch SSH public keys from IPA so users can log in with the key stored
|
# 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
|
# in their IPA profile rather than needing ~/.ssh/authorized_keys on every
|
||||||
# host. sss_ssh_authorizedkeys queries SSSD (which queries IPA LDAP).
|
# host. sss_ssh_authorizedkeys queries SSSD (which queries IPA LDAP).
|
||||||
@@ -58,7 +86,7 @@ lib.mkIf enabled {
|
|||||||
# AuthorizedKeysCommand binaries whose path contains any group-writable
|
# AuthorizedKeysCommand binaries whose path contains any group-writable
|
||||||
# component, silently skipping the command. Copy to /usr/local/bin (all
|
# component, silently skipping the command. Copy to /usr/local/bin (all
|
||||||
# components root-owned, 755) so the path passes sshd's safety check.
|
# components root-owned, 755) so the path passes sshd's safety check.
|
||||||
systemd.tmpfiles.rules = [
|
tmpfiles.rules = [
|
||||||
"d /usr/local 0755 root root - -"
|
"d /usr/local 0755 root root - -"
|
||||||
"d /usr/local/bin 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"
|
"C+ /usr/local/bin/sss_ssh_authorizedkeys 0555 root root - ${pkgs.sssd}/bin/sss_ssh_authorizedkeys"
|
||||||
@@ -69,29 +97,6 @@ lib.mkIf enabled {
|
|||||||
"d /home/${vars.ipaUser} 0700 ${vars.ipaUser} ${vars.ipaUser} - -"
|
"d /home/${vars.ipaUser} 0700 ${vars.ipaUser} ${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.
|
|
||||||
sops.secrets."ipa-host-keytab" = {
|
|
||||||
sopsFile = keytabPath;
|
|
||||||
format = "binary";
|
|
||||||
path = "/etc/krb5.keytab";
|
|
||||||
owner = "root";
|
|
||||||
group = "root";
|
|
||||||
mode = "0600";
|
|
||||||
restartUnits = [ "sssd.service" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
# security.ipa enables Kerberos (security.krb5) which causes systemd to
|
# security.ipa enables Kerberos (security.krb5) which causes systemd to
|
||||||
# start auth-rpcgss-module.service and rpc-gssd.service for Kerberos NFS
|
# start auth-rpcgss-module.service and rpc-gssd.service for Kerberos NFS
|
||||||
# authentication. LXC containers can't load the auth_rpcgss kernel module
|
# authentication. LXC containers can't load the auth_rpcgss kernel module
|
||||||
@@ -102,7 +107,7 @@ lib.mkIf enabled {
|
|||||||
# text conflict and add ConditionVirtualization=!container alongside it so
|
# text conflict and add ConditionVirtualization=!container alongside it so
|
||||||
# the service is skipped (not failed) in containers that do have a keytab.
|
# the service is skipped (not failed) in containers that do have a keytab.
|
||||||
# Same fix for rpc-gssd.service which also fails in containers.
|
# Same fix for rpc-gssd.service which also fails in containers.
|
||||||
systemd.units = lib.mkIf config.boot.isContainer {
|
units = lib.mkIf config.boot.isContainer {
|
||||||
"auth-rpcgss-module.service" = {
|
"auth-rpcgss-module.service" = {
|
||||||
overrideStrategy = "asDropinIfExists";
|
overrideStrategy = "asDropinIfExists";
|
||||||
text = lib.mkForce ''
|
text = lib.mkForce ''
|
||||||
@@ -126,10 +131,33 @@ lib.mkIf enabled {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Home Manager config for the IPA primary user, applied on every enrolled
|
# home-manager-<user>.service fails on first enrollment because /home/wayne
|
||||||
# host. Manages what IPA doesn't: dotfiles, user-scoped packages, session
|
# doesn't exist until the user's first login (pam_mkhomedir creates it then).
|
||||||
# variables. Switch-nix/Test-nix/buildImage are system-wide (configuration.nix)
|
# ConditionPathExists makes systemd skip the service (exit 0, condition not
|
||||||
# so they don't need to be repeated here.
|
# 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
|
||||||
|
'';
|
||||||
|
|
||||||
|
# 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 = keytabPath;
|
||||||
|
format = "binary";
|
||||||
|
path = "/etc/krb5.keytab";
|
||||||
|
owner = "root";
|
||||||
|
group = "root";
|
||||||
|
mode = "0600";
|
||||||
|
restartUnits = [ "sssd.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
# NixOS requires isNormalUser/isSystemUser + group on any entry in
|
# NixOS requires isNormalUser/isSystemUser + group on any entry in
|
||||||
# users.users. HM with useUserPackages = true (set in flake.nix) adds a stub
|
# users.users. HM with useUserPackages = true (set in flake.nix) adds a stub
|
||||||
# entry for each HM user so it can install packages to
|
# entry for each HM user so it can install packages to
|
||||||
@@ -137,15 +165,6 @@ lib.mkIf enabled {
|
|||||||
# With security.ipa setting "passwd: sss files" in nsswitch, SSSD's IPA entry
|
# With security.ipa setting "passwd: sss files" in nsswitch, SSSD's IPA entry
|
||||||
# takes priority for NSS lookups — this local stub is only a fallback when
|
# takes priority for NSS lookups — this local stub is only a fallback when
|
||||||
# SSSD is unreachable (at which point auth fails anyway).
|
# SSSD is unreachable (at which point auth fails anyway).
|
||||||
# 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.
|
|
||||||
users.users.${vars.ipaUser} = {
|
users.users.${vars.ipaUser} = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
group = "users";
|
group = "users";
|
||||||
@@ -153,19 +172,6 @@ lib.mkIf enabled {
|
|||||||
createHome = false;
|
createHome = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
# 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.
|
|
||||||
systemd.services."home-manager-${vars.ipaUser}".unitConfig.ConditionPathExists =
|
|
||||||
"/home/${vars.ipaUser}";
|
|
||||||
|
|
||||||
security.sudo.extraRules = [{
|
|
||||||
users = [ vars.ipaUser ];
|
|
||||||
commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
|
|
||||||
}];
|
|
||||||
|
|
||||||
# Home Manager config for the IPA primary user, applied on every enrolled
|
# Home Manager config for the IPA primary user, applied on every enrolled
|
||||||
# host. Manages what IPA doesn't: dotfiles, user-scoped packages, session
|
# host. Manages what IPA doesn't: dotfiles, user-scoped packages, session
|
||||||
# variables. Switch-nix/Test-nix/buildImage are system-wide (configuration.nix)
|
# variables. Switch-nix/Test-nix/buildImage are system-wide (configuration.nix)
|
||||||
|
|||||||
@@ -85,6 +85,12 @@
|
|||||||
# that IPA itself doesn't cover: dotfiles, user packages, session variables.
|
# that IPA itself doesn't cover: dotfiles, user packages, session variables.
|
||||||
ipaUser = "wayne";
|
ipaUser = "wayne";
|
||||||
|
|
||||||
|
# GID of the IPA "docker-access" group (GID 50010 on the IPA server).
|
||||||
|
# The local "docker" group is pinned to this GID on every host that runs
|
||||||
|
# Docker so that IPA group membership alone grants docker socket access -
|
||||||
|
# no per-host users.groups.docker.members entry for the IPA user needed.
|
||||||
|
dockerAccessGid = 50010;
|
||||||
|
|
||||||
# HA file server cluster
|
# HA file server cluster
|
||||||
# LAN IPs (vmbr0 / ens18) — client-facing: iSCSI initiators, NFS, management.
|
# LAN IPs (vmbr0 / ens18) — client-facing: iSCSI initiators, NFS, management.
|
||||||
# Storage IPs (vmbr1 / ens19) — isolated internal bridge, used for DRBD
|
# Storage IPs (vmbr1 / ens19) — isolated internal bridge, used for DRBD
|
||||||
|
|||||||
Reference in New Issue
Block a user