Skip to content

Commit

Permalink
krb5: Adds kerberos service
Browse files Browse the repository at this point in the history
Ports the NixOS module for building krb5.conf
  • Loading branch information
jtebbi committed Dec 9, 2023
1 parent 4b9b83d commit ac1c5dc
Show file tree
Hide file tree
Showing 3 changed files with 361 additions and 0 deletions.
1 change: 1 addition & 0 deletions modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
./services/hercules-ci-agent
./services/ipfs.nix
./services/karabiner-elements
./services/krb5
./services/khd
./services/kwm
./services/lorri.nix
Expand Down
274 changes: 274 additions & 0 deletions modules/services/krb5/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
{ config, lib, pkgs, ... }:

with lib;

let

cfg = config.krb5;

filterEmbeddedMetadata = value:
if isAttrs value then
(filterAttrs
(attrName: attrValue: attrName != "_module" && attrValue != null) value)
else
value;

indent = " ";

mkRelation = name: value:
if (isList value) then
concatMapStringsSep "\n" (mkRelation name) value
else
"${name} = ${mkVal value}";

mkVal = value:
if (value == true) then
"true"
else if (value == false) then
"false"
else if (isInt value) then
(toString value)
else if (isAttrs value) then
let
configLines = concatLists
(map (splitString "\n") (mapAttrsToList mkRelation value));
in (concatStringsSep ''
${indent}'' ([ "{" ] ++ configLines)) + ''
}''
else
value;

mkMappedAttrsOrString = value:
concatMapStringsSep "\n"
(line: if builtins.stringLength line > 0 then "${indent}${line}" else line)
(splitString "\n" (if isAttrs value then
concatStringsSep "\n" (mapAttrsToList mkRelation value)
else
value));

in {

###### interface

options = {
krb5 = {
enable = mkEnableOption
(lib.mdDoc "building krb5.conf, configuration file for Kerberos V");

kerberos = mkOption {
type = types.package;
default = pkgs.krb5;
defaultText = literalExpression "pkgs.krb5";
example = literalExpression "pkgs.heimdal";
description = lib.mdDoc ''
The Kerberos implementation that will be present in
`environment.systemPackages` after enabling this
service.
'';
};

libdefaults = mkOption {
type = with types; either attrs lines;
default = { };
apply = attrs: filterEmbeddedMetadata attrs;
example = literalExpression ''
{
default_realm = "ATHENA.MIT.EDU";
};
'';
description = lib.mdDoc ''
Settings used by the Kerberos V5 library.
'';
};

realms = mkOption {
type = with types; either attrs lines;
default = { };
example = literalExpression ''
{
"ATHENA.MIT.EDU" = {
admin_server = "athena.mit.edu";
kdc = [
"athena01.mit.edu"
"athena02.mit.edu"
];
};
};
'';
apply = attrs: filterEmbeddedMetadata attrs;
description =
lib.mdDoc "Realm-specific contact information and settings.";
};

domain_realm = mkOption {
type = with types; either attrs lines;
default = { };
example = literalExpression ''
{
"example.com" = "EXAMPLE.COM";
".example.com" = "EXAMPLE.COM";
};
'';
apply = attrs: filterEmbeddedMetadata attrs;
description = lib.mdDoc ''
Map of server hostnames to Kerberos realms.
'';
};

capaths = mkOption {
type = with types; either attrs lines;
default = { };
example = literalExpression ''
{
"ATHENA.MIT.EDU" = {
"EXAMPLE.COM" = ".";
};
"EXAMPLE.COM" = {
"ATHENA.MIT.EDU" = ".";
};
};
'';
apply = attrs: filterEmbeddedMetadata attrs;
description = lib.mdDoc ''
Authentication paths for non-hierarchical cross-realm authentication.
'';
};

appdefaults = mkOption {
type = with types; either attrs lines;
default = { };
example = literalExpression ''
{
pam = {
debug = false;
ticket_lifetime = 36000;
renew_lifetime = 36000;
max_timeout = 30;
timeout_shift = 2;
initial_timeout = 1;
};
};
'';
apply = attrs: filterEmbeddedMetadata attrs;
description = lib.mdDoc ''
Settings used by some Kerberos V5 applications.
'';
};

plugins = mkOption {
type = with types; either attrs lines;
default = { };
example = literalExpression ''
{
ccselect = {
disable = "k5identity";
};
};
'';
apply = attrs: filterEmbeddedMetadata attrs;
description = lib.mdDoc ''
Controls plugin module registration.
'';
};

extraConfig = mkOption {
type = with types; nullOr lines;
default = null;
example = ''
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
'';
description = lib.mdDoc ''
These lines go to the end of `krb5.conf` verbatim.
`krb5.conf` may include any of the relations that are
valid for `kdc.conf` (see `man kdc.conf`),
but it is not a recommended practice.
'';
};

config = mkOption {
type = with types; nullOr lines;
default = null;
example = ''
[libdefaults]
default_realm = EXAMPLE.COM
[realms]
EXAMPLE.COM = {
admin_server = kerberos.example.com
kdc = kerberos.example.com
default_principal_flags = +preauth
}
[domain_realm]
example.com = EXAMPLE.COM
.example.com = EXAMPLE.COM
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
'';
description = lib.mdDoc ''
Verbatim `krb5.conf` configuration. Note that this
is mutually exclusive with configuration via
`libdefaults`, `realms`,
`domain_realm`, `capaths`,
`appdefaults`, `plugins` and
`extraConfig` configuration options. Consult
`man krb5.conf` for documentation.
'';
};
};
};

###### implementation

config = mkIf cfg.enable {

environment.systemPackages = [ cfg.kerberos ];

environment.etc."krb5.conf".text = if isString cfg.config then
cfg.config
else
(''
[libdefaults]
${mkMappedAttrsOrString cfg.libdefaults}
[realms]
${mkMappedAttrsOrString cfg.realms}
[domain_realm]
${mkMappedAttrsOrString cfg.domain_realm}
[capaths]
${mkMappedAttrsOrString cfg.capaths}
[appdefaults]
${mkMappedAttrsOrString cfg.appdefaults}
[plugins]
${mkMappedAttrsOrString cfg.plugins}
'' + optionalString (cfg.extraConfig != null) ("\n" + cfg.extraConfig));

assertions = [{
assertion = !(cfg.config != null && ((builtins.any (value: value != { }) [
cfg.libdefaults
cfg.realms
cfg.domain_realm
cfg.capaths
cfg.appdefaults
cfg.plugins
]) || cfg.extraConfig != null));
message = ''
Configuration of krb5.conf using krb.config is mutually exclusive with
configuration by section. If you want to mix the two, you can pass
lines to any configuration section or lines to krb5.extraConfig.
'';
}];
};
}
86 changes: 86 additions & 0 deletions tests/krb5.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{ config, pkgs, ... }: {
krb5 = {
enable = true;
kerberos = pkgs.krb5;
libdefaults = { default_realm = "ATHENA.MIT.EDU"; };
realms = {
"ATHENA.MIT.EDU" = {
admin_server = "athena.mit.edu";
kdc = [ "athena01.mit.edu" "athena02.mit.edu" ];
};
};
domain_realm = {
"example.com" = "EXAMPLE.COM";
".example.com" = "EXAMPLE.COM";
};
capaths = {
"ATHENA.MIT.EDU" = { "EXAMPLE.COM" = "."; };
"EXAMPLE.COM" = { "ATHENA.MIT.EDU" = "."; };
};
appdefaults = {
pam = {
debug = false;
ticket_lifetime = 36000;
renew_lifetime = 36000;
max_timeout = 30;
timeout_shift = 2;
initial_timeout = 1;
};
};
plugins = { ccselect = { disable = "k5identity"; }; };
extraConfig = ''
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
'';
};
test = let
snapshot = pkgs.writeText "krb5-with-example-config.conf" ''
[libdefaults]
default_realm = ATHENA.MIT.EDU
[realms]
ATHENA.MIT.EDU = {
admin_server = athena.mit.edu
kdc = athena01.mit.edu
kdc = athena02.mit.edu
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
[capaths]
ATHENA.MIT.EDU = {
EXAMPLE.COM = .
}
EXAMPLE.COM = {
ATHENA.MIT.EDU = .
}
[appdefaults]
pam = {
debug = false
initial_timeout = 1
max_timeout = 30
renew_lifetime = 36000
ticket_lifetime = 36000
timeout_shift = 2
}
[plugins]
ccselect = {
disable = k5identity
}
[logging]
kdc = SYSLOG:NOTICE
admin_server = SYSLOG:NOTICE
default = SYSLOG:NOTICE
'';
in ''
echo "checking correctness of krb5.conf" >&2
diff /etc/krb5.conf ${snapshot}
'';
}

0 comments on commit ac1c5dc

Please sign in to comment.