Archived
Merge pull request 'feat(ipa/docker): grant docker access via IPA group membership' (#90) from worktree-docker-ipa-group into main
Check NixOS configurations / eval-hosts (push) Successful in 10m33s
Check NixOS configurations / eval-hosts (push) Successful in 10m33s
Reviewed-on: #90
This commit was merged in pull request #90.
This commit is contained in:
@@ -33,9 +33,6 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
users.users.${vars.primaryUser}.extraGroups = [ "docker" ];
|
users.users.${vars.primaryUser}.extraGroups = [ "docker" ];
|
||||||
# Grant the IPA domain user docker access via the local group so that
|
|
||||||
# `wayne` can manage containers without sudo.
|
|
||||||
users.groups.docker.members = [ "wayne" ];
|
|
||||||
services.openssh.settings.PermitRootLogin = "yes";
|
services.openssh.settings.PermitRootLogin = "yes";
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
{ pkgs, vars, ... }:
|
{ lib, pkgs, vars, ... }:
|
||||||
|
|
||||||
{
|
{
|
||||||
virtualisation.docker = {
|
virtualisation.docker = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.docker;
|
package = pkgs.docker;
|
||||||
};
|
};
|
||||||
|
# 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
|
||||||
|
|||||||
+99
-93
@@ -38,47 +38,113 @@ 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 = {
|
||||||
enable = true;
|
ipa = {
|
||||||
domain = vars.homeDomain;
|
enable = true;
|
||||||
inherit realm;
|
domain = vars.homeDomain;
|
||||||
server = vars.ipaServer;
|
inherit realm;
|
||||||
certificate = caCertPkg;
|
server = vars.ipaServer;
|
||||||
inherit basedn;
|
certificate = caCertPkg;
|
||||||
ipaHostname = fqdn;
|
inherit basedn;
|
||||||
offlinePasswords = true;
|
ipaHostname = fqdn;
|
||||||
cacheCredentials = true;
|
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
|
systemd = {
|
||||||
# in their IPA profile rather than needing ~/.ssh/authorized_keys on every
|
# Fetch SSH public keys from IPA so users can log in with the key stored
|
||||||
# host. sss_ssh_authorizedkeys queries SSSD (which queries IPA LDAP).
|
# 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
|
# /nix/store is 1775 (group-writable by nixbld). OpenSSH 10.0+ rejects
|
||||||
# component, silently skipping the command. Copy to /usr/local/bin (all
|
# AuthorizedKeysCommand binaries whose path contains any group-writable
|
||||||
# components root-owned, 755) so the path passes sshd's safety check.
|
# component, silently skipping the command. Copy to /usr/local/bin (all
|
||||||
systemd.tmpfiles.rules = [
|
# components root-owned, 755) so the path passes sshd's safety check.
|
||||||
"d /usr/local 0755 root root - -"
|
tmpfiles.rules = [
|
||||||
"d /usr/local/bin 0755 root root - -"
|
"d /usr/local 0755 root root - -"
|
||||||
"C+ /usr/local/bin/sss_ssh_authorizedkeys 0555 root root - ${pkgs.sssd}/bin/sss_ssh_authorizedkeys"
|
"d /usr/local/bin 0755 root root - -"
|
||||||
# Pre-create the IPA user's home dir so Home Manager activation succeeds
|
"C+ /usr/local/bin/sss_ssh_authorizedkeys 0555 root root - ${pkgs.sssd}/bin/sss_ssh_authorizedkeys"
|
||||||
# even before their first login. On a fresh system SSSD may not have
|
# Pre-create the IPA user's home dir so Home Manager activation succeeds
|
||||||
# resolved the user yet — tmpfiles warns and skips in that case (non-fatal),
|
# even before their first login. On a fresh system SSSD may not have
|
||||||
# and pam_mkhomedir covers the first-login path as a fallback.
|
# resolved the user yet — tmpfiles warns and skips in that case (non-fatal),
|
||||||
"d /home/${vars.ipaUser} 0700 ${vars.ipaUser} ${vars.ipaUser} - -"
|
# 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 = ''
|
services.openssh.extraConfig = ''
|
||||||
AuthorizedKeysCommand /usr/local/bin/sss_ssh_authorizedkeys %u
|
AuthorizedKeysCommand /usr/local/bin/sss_ssh_authorizedkeys %u
|
||||||
AuthorizedKeysCommandUser nobody
|
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.
|
# Host keytab: pre-provisioned on the IPA server, sops-encrypted binary.
|
||||||
# Placed at /etc/krb5.keytab before SSSD starts so the host authenticates
|
# Placed at /etc/krb5.keytab before SSSD starts so the host authenticates
|
||||||
# to IPA without running ipa-client-install.
|
# to IPA without running ipa-client-install.
|
||||||
@@ -92,44 +158,6 @@ lib.mkIf enabled {
|
|||||||
restartUnits = [ "sssd.service" ];
|
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
|
# 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