Set up nix tooling

This commit is contained in:
2025-03-26 12:25:38 +01:00
parent 3fba586bf5
commit 5b9d81284c
11 changed files with 1078 additions and 0 deletions

69
nix/module/default.nix Normal file
View File

@@ -0,0 +1,69 @@
{
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types;
cfg = config.services.kerberos_server;
inherit (config.security.krb5) package;
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
enableKdcACLEntries = true;
};
in
{
imports = [
(lib.mkRenamedOptionModule
[ "services" "kerberos_server" "realms" ]
[ "services" "kerberos_server" "settings" "realms" ]
)
# ./mit.nix
./heimdal.nix
];
options = {
services.kerberos_server = {
enable = lib.mkEnableOption "the kerberos authentication server";
settings = mkOption {
type = format.type;
description = ''
Settings for the kerberos server of choice.
See the following documentation:
- Heimdal: {manpage}`kdc.conf(5)`
- MIT Kerberos: <https://web.mit.edu/kerberos/krb5-1.21/doc/admin/conf_files/kdc_conf.html>
'';
default = { };
};
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ package ];
assertions = [
{
assertion = cfg.settings.realms != { };
message = "The server needs at least one realm";
}
{
assertion = lib.length (lib.attrNames cfg.settings.realms) <= 1;
message = "Only one realm per server is currently supported.";
}
];
systemd.slices.system-kerberos-server = { };
systemd.targets.kerberos-server = {
wantedBy = [ "multi-user.target" ];
};
};
# meta = {
# doc = ./kerberos-server.md;
# };
}

105
nix/module/heimdal.nix Normal file
View File

@@ -0,0 +1,105 @@
{
pkgs,
config,
lib,
...
}:
let
inherit (lib) mapAttrs;
cfg = config.services.kerberos_server;
package = config.security.krb5.package;
aclConfigs = lib.pipe cfg.settings.realms [
(mapAttrs (
name:
{ acl, ... }:
lib.concatMapStringsSep "\n" (
{
principal,
access,
target,
...
}:
"${principal}\t${lib.concatStringsSep "," (lib.toList access)}\t${target}"
) acl
))
(lib.mapAttrsToList (
name: text: {
dbname = "/var/lib/heimdal/heimdal";
acl_file = pkgs.writeText "${name}.acl" text;
}
))
];
finalConfig = cfg.settings // {
realms = mapAttrs (_: v: removeAttrs v [ "acl" ]) (cfg.settings.realms or { });
kdc = (cfg.settings.kdc or { }) // {
database = aclConfigs;
};
};
format = import ./krb5-conf-format.nix { inherit pkgs lib; } {
enableKdcACLEntries = true;
};
kdcConfFile = format.generate "kdc.conf" finalConfig;
in
{
config = lib.mkIf (cfg.enable && package.passthru.implementation == "heimdal") {
environment.etc."heimdal-kdc/kdc.conf".source = kdcConfFile;
systemd.tmpfiles.settings."10-heimdal" =
let
databases = lib.pipe finalConfig.kdc.database [
(map (dbAttrs: dbAttrs.dbname or null))
(lib.filter (x: x != null))
lib.unique
];
in
lib.genAttrs databases (_: {
d = {
user = "root";
group = "root";
mode = "0700";
};
});
systemd.services.kadmind = {
description = "Kerberos Administration Daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "kerberos-server.target" ];
serviceConfig = {
ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
Slice = "system-kerberos-server.slice";
StateDirectory = "heimdal";
};
restartTriggers = [ kdcConfFile ];
};
systemd.services.kdc = {
description = "Key Distribution Center daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "kerberos-server.target" ];
serviceConfig = {
ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
Slice = "system-kerberos-server.slice";
StateDirectory = "heimdal";
};
restartTriggers = [ kdcConfFile ];
};
systemd.services.kpasswdd = {
description = "Kerberos Password Changing daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "kerberos-server.target" ];
serviceConfig = {
ExecStart = "${package}/libexec/kpasswdd";
Slice = "system-kerberos-server.slice";
StateDirectory = "heimdal";
};
restartTriggers = [ kdcConfFile ];
};
};
}

View File

@@ -0,0 +1,204 @@
{ pkgs, lib, ... }:
# Based on
# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html
# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html
let
inherit (lib)
boolToString
concatMapStringsSep
concatStringsSep
filter
isAttrs
isBool
isList
mapAttrsToList
mkOption
singleton
splitString
;
inherit (lib.types)
attrsOf
bool
coercedTo
either
enum
int
listOf
oneOf
path
str
submodule
;
in
{
enableKdcACLEntries ? false,
}:
rec {
sectionType =
let
relation = oneOf [
(listOf (attrsOf value))
(attrsOf value)
value
];
value = either (listOf atom) atom;
atom = oneOf [
int
str
bool
];
in
attrsOf relation;
type =
let
aclEntry = submodule {
options = {
principal = mkOption {
type = str;
description = "Which principal the rule applies to";
};
access = mkOption {
type = either (listOf (enum [
"add"
"cpw"
"delete"
"get"
"list"
"modify"
])) (enum [ "all" ]);
default = "all";
description = "The changes the principal is allowed to make.";
};
target = mkOption {
type = str;
default = "*";
description = "The principals that 'access' applies to.";
};
};
};
realm = submodule (
{ name, ... }:
{
freeformType = sectionType;
options = {
acl = mkOption {
type = listOf aclEntry;
default = [
{
principal = "*/admin";
access = "all";
}
{
principal = "admin";
access = "all";
}
];
description = ''
The privileges granted to a user.
'';
};
};
}
);
in
submodule {
freeformType = attrsOf sectionType;
options =
{
include = mkOption {
default = [ ];
description = ''
Files to include in the Kerberos configuration.
'';
type = coercedTo path singleton (listOf path);
};
includedir = mkOption {
default = [ ];
description = ''
Directories containing files to include in the Kerberos configuration.
'';
type = coercedTo path singleton (listOf path);
};
module = mkOption {
default = [ ];
description = ''
Modules to obtain Kerberos configuration from.
'';
type = coercedTo path singleton (listOf path);
};
}
// (lib.optionalAttrs enableKdcACLEntries {
realms = mkOption {
type = attrsOf realm;
description = ''
The realm(s) to serve keys for.
'';
};
});
};
generate =
let
indent = str: concatMapStringsSep "\n" (line: " " + line) (splitString "\n" str);
formatToplevel =
args@{
include ? [ ],
includedir ? [ ],
module ? [ ],
...
}:
let
sections = removeAttrs args [
"include"
"includedir"
"module"
];
in
concatStringsSep "\n" (
filter (x: x != "") [
(concatStringsSep "\n" (mapAttrsToList formatSection sections))
(concatMapStringsSep "\n" (m: "module ${m}") module)
(concatMapStringsSep "\n" (i: "include ${i}") include)
(concatMapStringsSep "\n" (i: "includedir ${i}") includedir)
]
);
formatSection = name: section: ''
[${name}]
${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))}
'';
formatRelation =
name: relation:
if isAttrs relation then
''
${name} = {
${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
}''
else if isList relation then
concatMapStringsSep "\n" (formatRelation name) relation
else
formatValue name relation;
formatValue =
name: value:
if isList value then concatMapStringsSep "\n" (formatAtom name) value else formatAtom name value;
formatAtom =
name: atom:
let
v = if isBool atom then boolToString atom else toString atom;
in
"${name} = ${v}";
in
name: value:
pkgs.writeText name ''
${formatToplevel value}
'';
}