fix(nfs): sec=krb5

This commit is contained in:
arcnmx 2024-03-17 21:16:46 -07:00
parent 7cbdb4c231
commit 88477df521
6 changed files with 336 additions and 64 deletions

View file

@ -0,0 +1,89 @@
{
config,
lib,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkAfter mkOptionDefault;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) concatStringsSep;
inherit (config.system) nssDatabases;
inherit (config) networking;
netgroupMemberModule = { config, name, ... }: {
options = with lib.types; {
hostname = mkOption {
type = str;
default = name;
};
user = mkOption {
type = either (enum [ null "-" ]) str;
default = "-";
};
domain = mkOption {
type = str;
default = networking.domain;
description = "NIS domain";
};
triple = mkOption {
type = str;
};
};
config = {
triple = mkOptionDefault "(${config.hostname},${toString config.user},${config.domain})";
};
};
netgroupModule = { config, name, ... }: {
options = with lib.types; {
name = mkOption {
type = str;
default = name;
};
members = mkOption {
type = attrsOf (submodule netgroupMemberModule);
default = { };
};
fileLine = mkOption {
type = str;
};
};
config = {
fileLine = mkOptionDefault (concatStringsSep " " ([ config.name ] ++ mapAttrsToList (_: member: member.triple) config.members));
};
};
in {
options = with lib.types; {
system.nssDatabases = {
netgroup = mkOption {
type = listOf str;
};
};
networking = {
netgroups = mkOption {
type = attrsOf (submodule netgroupModule);
default = { };
};
extraNetgroups = mkOption {
type = lines;
default = "";
};
};
};
config = {
system.nssDatabases = {
netgroup = mkMerge [
(mkBefore [ "files" ])
(mkAfter [ "nis" ])
(mkIf config.services.sssd.enable [ "sss" ])
];
};
environment.etc."nssswitch.conf".text = mkIf (nssDatabases.netgroup != [ ]) (mkAfter ''
netgroup: ${concatStringsSep " " nssDatabases.netgroup}
'');
environment.etc."netgroup" = mkIf (networking.netgroups != { } || networking.extraNetgroups != "") {
text = mkMerge (
mapAttrsToList (_: ng: ng.fileLine) networking.netgroups
++ [ networking.extraNetgroups ]
);
};
};
}

139
modules/nixos/nfs.nix Normal file
View file

@ -0,0 +1,139 @@
{
pkgs,
config,
lib,
...
}: let
inherit (lib.options) mkOption;
inherit (lib.modules) mkMerge mkIf mkBefore mkForce mkOptionDefault;
inherit (lib.lists) optional;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.lists) toList;
inherit (lib.strings) optionalString concatStringsSep concatMapStringsSep;
cfg = config.services.nfs;
clientEnabled = config.boot.supportedFilesystems.nfs or config.boot.supportedFilesystems.nfs4 or false;
enabled = cfg.server.enable || clientEnabled;
openPorts = [
(mkIf cfg.server.enable 2049)
(mkIf config.services.rpcbind.enable 111)
(mkIf (cfg.server.statdPort != null) cfg.server.statdPort)
(mkIf (cfg.server.lockdPort != null) cfg.server.lockdPort)
(mkIf (cfg.server.mountdPort != null) cfg.server.mountdPort)
];
concatFlags = concatStringsSep ",";
clientModule = { config, name, ... }: {
options = with lib.types; {
machine = mkOption {
type = oneOf [ str (listOf str) ];
default = name;
example = "*";
};
flags = mkOption {
type = listOf str;
default = [ ];
};
entry = mkOption {
type = str;
};
};
config = {
entry = let
flags = optionalString (config.flags != [ ]) "(${concatFlags config.flags})";
machines = toList config.machine;
in mkOptionDefault (concatMapStringsSep " " (machine: machine + flags) machines);
};
};
exportModule = { config, name, ... }: {
options = with lib.types; {
path = mkOption {
type = path;
default = name;
};
flags = mkOption {
type = listOf str;
};
clients = mkOption {
type = attrsOf (submodule clientModule);
};
fileLine = mkOption {
type = str;
};
};
config = {
flags = mkOptionDefault (cfg.export.flagSets.common or [ ]);
fileLine = let
parts = [ config.path ]
++ optional (config.flags != [ ]) "-${concatFlags config.flags}"
++ mapAttrsToList (_: client: client.entry) config.clients;
in mkOptionDefault (concatStringsSep " " parts);
};
};
in {
options.services.nfs = with lib.types; {
export = {
flagSets = mkOption {
type = lazyAttrsOf (listOf str);
default = {
common = [ "no_subtree_check" ];
};
};
root = mkOption {
type = nullOr (submodule [
exportModule
({ ... }: {
flags = mkMerge [
(cfg.export.flagSets.common or [ ])
];
})
]);
default = null;
};
paths = mkOption {
type = attrsOf (submodule exportModule);
default = { };
};
};
};
config = {
services.nfs = {
server.exports = mkMerge (
optional (cfg.export.root != null) (mkBefore cfg.export.root.fileLine)
++ mapAttrsToList (_: export: export.fileLine) cfg.export.paths
);
};
networking.firewall.interfaces.local = mkIf enabled {
allowedTCPPorts = openPorts;
allowedUDPPorts = openPorts;
};
systemd.services = {
auth-rpcgss-module = mkIf (enabled && !config.boot.modprobeConfig.enable) {
serviceConfig.ExecStart = mkForce [
""
"${pkgs.coreutils}/bin/true"
];
};
rpc-svcgssd = mkIf enabled {
enable = mkIf (!cfg.server.enable) false;
wantedBy = mkIf (cfg.server.enable && (config.security.krb5.enable || config.security.ipa.enable)) [
"nfs-server.service"
];
};
nfs-mountd = mkIf cfg.server.enable {
environment.LD_LIBRARY_PATH = config.system.nssModules.path;
};
};
systemd.mounts = mkIf (cfg.server.enable && cfg.export.root != null) [
rec {
type = "tmpfs";
options = "rw,size=256k";
what = "none";
where = cfg.export.root.path;
requiredBy = [
"nfs-server.service"
"nfs-mountd.service"
];
before = requiredBy;
}
];
};
}

View file

@ -195,8 +195,7 @@ in {
map $ssl_preread_server_name $ldap_upstream {
hostnames;
# TODO: ${access.domain} ${upstreams.ldap_freeipa};
${access.globalDomain} ${upstreams.ldap_freeipa};
${access.domain} ${upstreams.ldap_freeipa};
default ${upstreams.ldap};
}

View file

@ -1,7 +1,7 @@
{ inputs, pkgs, config, lib, ... }: let
inherit (inputs.self.lib.lib) mkBaseDn;
inherit (lib.modules) mkIf mkForce mkDefault;
inherit (lib.strings) toUpper splitString concatMapStringsSep;
inherit (lib.modules) mkIf mkBefore mkForce mkDefault;
inherit (lib.strings) toUpper;
inherit (config.networking) domain;
cfg = config.security.ipa;
baseDn = mkBaseDn domain;
@ -32,14 +32,8 @@ in {
] ++ config.users.groups.wheel.members;
dyndns.enable = mkDefault false;
};
networking.extraHosts = mkIf cfg.enable ''
10.1.1.46 idp.${domain}
'';
systemd.services.auth-rpcgss-module = mkIf (cfg.enable && !config.boot.modprobeConfig.enable) {
serviceConfig.ExecStart = mkForce [
""
"${pkgs.coreutils}/bin/true"
];
networking.hosts = mkIf cfg.enable {
"10.1.1.46" = mkBefore [ "idp.${domain}" ];
};
sops.secrets = {
krb5-keytab = mkIf cfg.enable {

View file

@ -3,48 +3,60 @@
lib,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.lists) optionals;
inherit (lib.strings) concatStringsSep;
inherit (config.networking.access) cidrForNetwork;
inherit (config) kyuuto;
inherit (config.services.nfs.export) flagSets;
nfsRoot = {
__toString = _: config.services.nfs.export.root.path;
transfer = "${nfsRoot}/kyuuto/transfer";
media = "${nfsRoot}/kyuuto/media";
};
in {
services.nfs.server.exports = let
mapPerm = perm: map (addr: "${addr}(${concatStringsSep "," perm})");
toPerms = concatStringsSep " ";
localAddrs = cidrForNetwork.loopback.all ++ cidrForNetwork.local.all;
tailAddrs = optionals config.services.tailscale.enable cidrForNetwork.tail.all;
allAddrs = localAddrs ++ tailAddrs;
globalAddrs = [
"@peeps"
services.nfs = {
export = {
paths = {
${nfsRoot.media} = {
flags = flagSets.common ++ [ "fsid=128" ] ++ flagSets.secip ++ [ "rw" ] ++ flagSets.anon_ro;
clients = {
local = {
machine = flagSets.allClients;
flags = flagSets.seclocal ++ [ "rw" "no_all_squash" ];
};
};
};
${nfsRoot.transfer} = {
flags = flagSets.common ++ [ "fsid=129" ] ++ [ "rw" "async" ];
clients = {
local = {
machine = flagSets.allClients;
flags = flagSets.secanon;
};
};
};
};
};
};
systemd.mounts = let
type = "none";
options = "bind";
wantedBy = [
"nfs-server.service"
"nfs-mountd.service"
];
before = wantedBy;
in mkIf config.services.nfs.server.enable [
{
inherit type options wantedBy before;
what = kyuuto.mountDir;
where = nfsRoot.media;
}
{
inherit type options wantedBy before;
what = kyuuto.transferDir;
where = nfsRoot.transfer;
}
];
common = [
"no_subtree_check"
];
sec = [
"sec=${concatStringsSep ":" [ "krb5i" "krb5" "krb5p" ]}"
# TODO: no_root_squash..?
];
anon = [
"sec=sys"
"all_squash"
"anonuid=${toString config.users.users.guest.uid}"
"anongid=${toString config.users.groups.${config.users.users.guest.group}.gid}"
];
# TODO: this can be simplified by specifying `sec=` multiple times, with restrictive options following sec=sys,all_squash,ro,etc
kyuutoOpts = common;
kyuutoPerms =
mapPerm (kyuutoOpts ++ [ "rw" ] ++ sec) globalAddrs
++ mapPerm (kyuutoOpts ++ [ "ro" ] ++ anon) localAddrs
# XXX: remove me once kerberos is set up!
++ mapPerm (kyuutoOpts ++ [ "rw" "sec=sys" ]) tailAddrs
;
transferOpts = common ++ [ "rw" "async" ];
transferPerms =
mapPerm (transferOpts ++ sec) globalAddrs
++ mapPerm (transferOpts ++ anon) allAddrs
;
in ''
${kyuuto.mountDir} ${toPerms kyuutoPerms}
${kyuuto.transferDir} ${toPerms transferPerms}
'';
}

View file

@ -6,27 +6,70 @@
}: let
inherit (inputs.self.lib.lib) mkBaseDn;
inherit (lib.modules) mkIf mkForce mkDefault;
inherit (lib.lists) optional;
inherit (lib.strings) toUpper concatStringsSep concatMapStringsSep splitString;
inherit (lib.lists) optional optionals;
inherit (lib.strings) toUpper concatStringsSep;
inherit (config.networking.access) cidrForNetwork;
cfg = config.services.nfs;
inherit (cfg.export) flagSets;
inherit (config.networking) domain;
openPorts = [
(mkIf cfg.server.enable 2049)
(mkIf config.services.rpcbind.enable 111)
(mkIf (cfg.server.statdPort != null) cfg.server.statdPort)
(mkIf (cfg.server.lockdPort != null) cfg.server.lockdPort)
(mkIf (cfg.server.mountdPort != null) cfg.server.mountdPort)
];
enableLdap = false;
baseDn = mkBaseDn domain;
in {
services.nfs = {
config.services.nfs = {
server = {
enable = mkDefault true;
statdPort = mkDefault 4000;
lockdPort = mkDefault 4001;
mountdPort = mkDefault 4002;
};
export = {
flagSets = let
localAddrs = cidrForNetwork.loopback.all ++ cidrForNetwork.local.all;
in {
common = [
"no_subtree_check"
"anonuid=${toString config.users.users.guest.uid}"
"anongid=${toString config.users.groups.${config.users.users.guest.group}.gid}"
];
sec = [
"sec=${concatStringsSep ":" [ "krb5i" "krb5" "krb5p" ]}"
];
seclocal = [
"sec=${concatStringsSep ":" [ "krb5" ]}"
];
secip = [
"sec=${concatStringsSep ":" [ "krb5i" "krb5p" ]}"
];
secanon = [
"sec=${concatStringsSep ":" [ "krb5i" "krb5" "krb5p" "sys" ]}"
];
anon_ro = [
"sec=sys"
"all_squash"
"ro"
];
# client machines
clientGroups = [
"@peeps"
"@infra"
];
trustedClients = [
"@trusted"
];
tailClients = optionals config.services.tailscale.enable cidrForNetwork.tail.all;
localClients = localAddrs ++ flagSets.tailClients;
allClients = flagSets.clientGroups ++ flagSets.trustedClients ++ flagSets.localClients;
};
root = {
path = "/srv/fs";
clients = {
trusted = {
machine = flagSets.trustedClients;
flags = flagSets.secip ++ [ "rw" ];
};
};
};
};
idmapd.settings = {
General = {
Domain = mkForce domain;
@ -61,8 +104,4 @@ in {
};
};
};
networking.firewall.interfaces.local = {
allowedTCPPorts = openPorts;
allowedUDPPorts = openPorts;
};
}