Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
58b4b1f06e
|
|||
|
583408b3e3
|
|||
|
f8cc75b9c6
|
|||
|
69e74be224
|
|||
|
f6ec5e5f9a
|
|||
|
049748c182
|
|||
|
85b816b5bc
|
|||
|
b795025613
|
|||
|
4cba7c91d1
|
@@ -694,3 +694,6 @@ asn1_*_asn1.c
|
||||
/tools/krb5-gssapi.pc
|
||||
/tools/krb5.pc
|
||||
/tools/krb5-config
|
||||
|
||||
result
|
||||
result-*
|
||||
|
||||
@@ -196,6 +196,22 @@ AM_CONDITIONAL([HAVE_CAPNG], [test "$with_capng" != "no"])
|
||||
AC_SUBST([CAPNG_CFLAGS])
|
||||
AC_SUBST([CAPNG_LIBS])
|
||||
|
||||
dnl libsystemd
|
||||
AC_ARG_WITH([systemd],
|
||||
AC_HELP_STRING([--with-systemd], [use libsystemd for sd_notify service notifications @<:@default=check@:>@]),
|
||||
[],
|
||||
[with_systemd=check])
|
||||
if test "$with_systemd" != "no"; then
|
||||
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
|
||||
[with_systemd=yes],[with_systemd=no])
|
||||
fi
|
||||
if test "$with_systemd" = "yes"; then
|
||||
AC_DEFINE_UNQUOTED([HAVE_SYSTEMD], 1, [whether libsystemd is available for sd_notify])
|
||||
fi
|
||||
AM_CONDITIONAL([HAVE_SYSTEMD], [test "$with_systemd" != "no"])
|
||||
AC_SUBST([SYSTEMD_CFLAGS])
|
||||
AC_SUBST([SYSTEMD_LIBS])
|
||||
|
||||
dnl mitdb
|
||||
AC_ARG_WITH([mitdb],
|
||||
AC_HELP_STRING([--with-mitdb], [Path to MIT Kerberos DB include header and shared object]),
|
||||
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1770380644,
|
||||
"narHash": "sha256-P7dWMHRUWG5m4G+06jDyThXO7kwSk46C1kgjEWcybkE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ae67888ff7ef9dff69b3cf0cc0fbfbcd3a722abe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
outputs = { self, nixpkgs }: let
|
||||
inherit (nixpkgs) lib;
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
|
||||
forAllSystems = f: lib.genAttrs systems (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
self.overlays.${system}.default
|
||||
];
|
||||
};
|
||||
in f system pkgs);
|
||||
in {
|
||||
devShells = forAllSystems (system: pkgs: {
|
||||
default = pkgs.callPackage ./nix/shell.nix { };
|
||||
});
|
||||
|
||||
packages = forAllSystems (system: pkgs: let
|
||||
src = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.difference ./. (lib.fileset.unions [
|
||||
./.github
|
||||
./.gitignore
|
||||
./flake.nix
|
||||
./flake.lock
|
||||
./nix
|
||||
]);
|
||||
};
|
||||
in {
|
||||
default = self.packages.${system}.heimdal;
|
||||
|
||||
src = pkgs.runCommand "heimdal-src" {} "ln -s ${src} \"$out\"";
|
||||
|
||||
heimdal = pkgs.callPackage ./nix/heimdal/package.nix {
|
||||
inherit src;
|
||||
inherit (pkgs.darwin.apple_sdk.frameworks) CoreFoundation Security SystemConfiguration;
|
||||
autoreconfHook = pkgs.buildPackages.autoreconfHook271;
|
||||
};
|
||||
|
||||
nixosTest = pkgs.testers.runNixOSTest (import ./nix/nixosTest.nix { inherit nixpkgs; });
|
||||
});
|
||||
|
||||
overlays = forAllSystems (system: pkgs: {
|
||||
default = final: prev: {
|
||||
heimdal = self.packages.${system}.heimdal;
|
||||
};
|
||||
});
|
||||
|
||||
nixosModules = {
|
||||
default = self.nixosModules.heimdal;
|
||||
heimdal = ./nix/module;
|
||||
};
|
||||
};
|
||||
}
|
||||
+4
-1
@@ -72,7 +72,10 @@ kadmind_LDADD = $(top_builddir)/lib/kadm5/libkadm5srv.la \
|
||||
../lib/gssapi/libgssapi.la \
|
||||
$(LDADD_common) \
|
||||
$(LIB_pidfile) \
|
||||
$(LIB_dlopen)
|
||||
$(LIB_dlopen) \
|
||||
$(SYSTEMD_LIBS)
|
||||
|
||||
kadmind_CFLAGS = $(SYSTEMD_CFLAGS)
|
||||
|
||||
kadmin_LDADD = \
|
||||
$(top_builddir)/lib/kadm5/libkadm5clnt.la \
|
||||
|
||||
+69
-8
@@ -35,6 +35,11 @@
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
/* how often (seconds) to refresh the sd_notify STATUS line while idle */
|
||||
#define STATUS_INTERVAL 30
|
||||
#endif
|
||||
|
||||
extern int daemon_child;
|
||||
|
||||
@@ -203,22 +208,52 @@ wait_for_connection(krb5_context contextp,
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGCHLD, sigchld);
|
||||
|
||||
{
|
||||
struct timeval *tmoutp = NULL;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
unsigned long nconns = 0;
|
||||
struct timeval tmout;
|
||||
|
||||
tmoutp = &tmout;
|
||||
sd_notify(0, "READY=1");
|
||||
sd_notifyf(0, "STATUS=Serving; %lu connection(s) accepted", nconns);
|
||||
#endif
|
||||
|
||||
while (term_flag == 0) {
|
||||
read_set = orig_read_set;
|
||||
e = select(max_fd + 1, &read_set, NULL, NULL, NULL);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
tmout.tv_sec = STATUS_INTERVAL;
|
||||
tmout.tv_usec = 0;
|
||||
#endif
|
||||
e = select(max_fd + 1, &read_set, NULL, NULL, tmoutp);
|
||||
if(rk_IS_SOCKET_ERROR(e)) {
|
||||
if(rk_SOCK_ERRNO != EINTR)
|
||||
krb5_warn(contextp, rk_SOCK_ERRNO, "select");
|
||||
} else if(e == 0)
|
||||
} else if(e == 0) {
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* select timed out: refresh our systemd status line */
|
||||
sd_notifyf(0, "STATUS=Serving; %lu connection(s) accepted", nconns);
|
||||
#else
|
||||
krb5_warnx(contextp, "select returned 0");
|
||||
else {
|
||||
#endif
|
||||
} else {
|
||||
for(i = 0; i < num_socks; i++) {
|
||||
if(FD_ISSET(socks[i], &read_set))
|
||||
if(FD_ISSET(socks[i], &read_set)) {
|
||||
if(spawn_child(contextp, socks, num_socks, i) == 0)
|
||||
return;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
nconns++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "STOPPING=1");
|
||||
#endif
|
||||
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
while ((waitpid(-1, &status, WNOHANG)) > 0)
|
||||
@@ -238,12 +273,37 @@ start_server(krb5_context contextp, const char *port_str)
|
||||
unsigned int num_socks = 0;
|
||||
int i;
|
||||
|
||||
if (port_str == NULL)
|
||||
port_str = "+";
|
||||
#ifdef HAVE_SYSTEMD
|
||||
{
|
||||
char **names = NULL;
|
||||
int sd_n, j;
|
||||
|
||||
parse_ports(contextp, port_str);
|
||||
sd_n = sd_listen_fds_with_names(0, &names);
|
||||
if (sd_n < 0)
|
||||
krb5_err(contextp, 1, -sd_n, "sd_listen_fds_with_names");
|
||||
if (sd_n > 0) {
|
||||
socks = malloc(sd_n * sizeof(*socks));
|
||||
if (socks == NULL)
|
||||
krb5_err(contextp, 1, errno, "malloc");
|
||||
for (j = 0; j < sd_n; j++) {
|
||||
if (names != NULL && names[j] != NULL &&
|
||||
strncmp(names[j], "kadmind", 7) == 0)
|
||||
socks[num_socks++] = SD_LISTEN_FDS_START + j;
|
||||
}
|
||||
}
|
||||
for (j = 0; names != NULL && names[j] != NULL; j++)
|
||||
free(names[j]);
|
||||
free(names);
|
||||
}
|
||||
#endif /* HAVE_SYSTEMD */
|
||||
|
||||
for(p = kadm_ports; p; p = p->next) {
|
||||
if (num_socks == 0) {
|
||||
if (port_str == NULL)
|
||||
port_str = "+";
|
||||
|
||||
parse_ports(contextp, port_str);
|
||||
|
||||
for(p = kadm_ports; p; p = p->next) {
|
||||
struct addrinfo hints, *ai, *ap;
|
||||
char portstr[32];
|
||||
memset (&hints, 0, sizeof(hints));
|
||||
@@ -299,6 +359,7 @@ start_server(krb5_context contextp, const char *port_str)
|
||||
socks[num_socks++] = s;
|
||||
}
|
||||
freeaddrinfo (ai);
|
||||
}
|
||||
}
|
||||
if(num_socks == 0)
|
||||
krb5_errx(contextp, 1, "no sockets to listen to - exiting");
|
||||
|
||||
+2
-2
@@ -198,12 +198,12 @@ LDADD = $(top_builddir)/lib/hdb/libhdb.la \
|
||||
$(LIB_roken) \
|
||||
$(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
|
||||
|
||||
kdc_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(CAPNG_LIBS)
|
||||
kdc_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(CAPNG_LIBS) $(SYSTEMD_LIBS)
|
||||
|
||||
if FRAMEWORK_SECURITY
|
||||
kdc_LDFLAGS = -framework SystemConfiguration -framework CoreFoundation
|
||||
endif
|
||||
kdc_CFLAGS = $(CAPNG_CFLAGS)
|
||||
kdc_CFLAGS = $(CAPNG_CFLAGS) $(SYSTEMD_CFLAGS)
|
||||
|
||||
kdc_replay_LDADD = libkdc.la $(LDADD) $(LIB_pidfile)
|
||||
kdc_tester_LDADD = libkdc.la $(LDADD) $(LIB_pidfile) $(LIB_heimbase)
|
||||
|
||||
+92
-1
@@ -33,6 +33,10 @@
|
||||
|
||||
#include "kdc_locl.h"
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* a tuple describing on what to listen
|
||||
*/
|
||||
@@ -295,6 +299,69 @@ init_socket(krb5_context context,
|
||||
socket_set_keepalive(d->s, 1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/*
|
||||
* Allocate descriptors for the sockets handed to us via systemd socket
|
||||
* activation whose FileDescriptorName is "kdc", and return then number of
|
||||
* them (0 if we were not socket-activated).
|
||||
*/
|
||||
|
||||
static int
|
||||
init_sockets_sd(krb5_context context,
|
||||
krb5_kdc_configuration *config,
|
||||
struct descr **desc)
|
||||
{
|
||||
char **names = NULL;
|
||||
struct descr *d;
|
||||
int n, i, num = 0;
|
||||
|
||||
n = sd_listen_fds_with_names(0, &names);
|
||||
if (n < 0)
|
||||
krb5_err(context, 1, -n, "sd_listen_fds_with_names");
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
d = malloc(n * sizeof(*d));
|
||||
if (d == NULL)
|
||||
krb5_errx(context, 1, "malloc(%lu) failed",
|
||||
(unsigned long)n * sizeof(*d));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
int fd = SD_LISTEN_FDS_START + i;
|
||||
int type;
|
||||
socklen_t tlen = sizeof(type);
|
||||
|
||||
if (names == NULL || names[i] == NULL ||
|
||||
strncmp(names[i], "kdc", 3) != 0)
|
||||
continue;
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &tlen) < 0) {
|
||||
krb5_warn(context, errno,
|
||||
"getsockopt(SO_TYPE) on socket-activated fd %d", fd);
|
||||
continue;
|
||||
}
|
||||
init_descr(&d[num]);
|
||||
d[num].s = fd;
|
||||
d[num].type = type;
|
||||
socket_set_nonblocking(fd, 1);
|
||||
kdc_log(context, config, 3, "listening on socket-activated fd %d (%s)",
|
||||
fd, (type == SOCK_STREAM) ? "tcp" : "udp");
|
||||
num++;
|
||||
}
|
||||
|
||||
for (i = 0; names != NULL && names[i] != NULL; i++)
|
||||
free(names[i]);
|
||||
free(names);
|
||||
|
||||
if (num == 0) {
|
||||
free(d);
|
||||
return 0;
|
||||
}
|
||||
reinit_descrs(d, num);
|
||||
*desc = d;
|
||||
return num;
|
||||
}
|
||||
#endif /* HAVE_SYSTEMD */
|
||||
|
||||
/*
|
||||
* Allocate descriptors for all the sockets that we should listen on
|
||||
* and return the number of them.
|
||||
@@ -1191,7 +1258,12 @@ start_kdc(krb5_context context,
|
||||
socket_set_nonblocking(islive[1], 1);
|
||||
#endif
|
||||
|
||||
ndescr = init_sockets(context, config, &d);
|
||||
ndescr = 0;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
ndescr = init_sockets_sd(context, config, &d);
|
||||
#endif
|
||||
if (ndescr <= 0)
|
||||
ndescr = init_sockets(context, config, &d);
|
||||
if(ndescr <= 0)
|
||||
krb5_errx(context, 1, "No sockets!");
|
||||
|
||||
@@ -1209,6 +1281,11 @@ start_kdc(krb5_context context,
|
||||
|
||||
roken_detach_finish(NULL, daemon_child);
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "READY=1");
|
||||
sd_notify(0, "STATUS=Serving requests");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FORK
|
||||
if (!testing_flag) {
|
||||
/* Note that we might never execute the body of this loop */
|
||||
@@ -1250,12 +1327,20 @@ start_kdc(krb5_context context,
|
||||
kdc_log(context, config, 3, "KDC worker process started: %d",
|
||||
pid);
|
||||
num_kdcs++;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notifyf(0, "STATUS=Serving requests; %d of %d KDC worker "
|
||||
"process(es) running", num_kdcs, max_kdcs);
|
||||
#endif
|
||||
/* Slow down the creation of KDCs... */
|
||||
select_sleep(12500);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "STOPPING=1");
|
||||
#endif
|
||||
|
||||
/* Closing these sockets should cause the kids to die... */
|
||||
|
||||
close(islive[0]);
|
||||
@@ -1307,11 +1392,17 @@ start_kdc(krb5_context context,
|
||||
kdc_log(context, config, 3, "KDC master process exiting");
|
||||
} else {
|
||||
loop(context, config, &d, &ndescr, -1);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "STOPPING=1");
|
||||
#endif
|
||||
kdc_log(context, config, 3, "KDC exiting");
|
||||
}
|
||||
free(pids);
|
||||
#else
|
||||
loop(context, config, &d, &ndescr, -1);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "STOPPING=1");
|
||||
#endif
|
||||
kdc_log(context, config, 3, "KDC exiting");
|
||||
#endif
|
||||
|
||||
|
||||
+4
-1
@@ -24,7 +24,10 @@ kpasswdd_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LIB_pidfile) \
|
||||
$(LIB_dlopen) \
|
||||
$(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
|
||||
$(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
|
||||
$(SYSTEMD_LIBS)
|
||||
|
||||
kpasswdd_CFLAGS = $(SYSTEMD_CFLAGS)
|
||||
|
||||
LDADD = $(top_builddir)/lib/krb5/libkrb5.la \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
|
||||
+150
-60
@@ -41,6 +41,11 @@ RCSID("$Id$");
|
||||
#include <hdb.h>
|
||||
#include <kadm5/private.h>
|
||||
#include <kadm5/kadm5_err.h>
|
||||
#ifdef HAVE_SYSTEMD
|
||||
#include <systemd/sd-daemon.h>
|
||||
/* how often (seconds) to refresh the sd_notify STATUS line while idle */
|
||||
#define STATUS_INTERVAL 30
|
||||
#endif
|
||||
|
||||
static krb5_context context;
|
||||
static krb5_log_facility *log_facility;
|
||||
@@ -701,82 +706,167 @@ doit(krb5_keytab keytab, int port)
|
||||
fd_set real_fdset;
|
||||
struct sockaddr_storage __ss;
|
||||
struct sockaddr *sa = (struct sockaddr *)&__ss;
|
||||
int sd_activated = 0;
|
||||
|
||||
if (explicit_addresses.len) {
|
||||
addrs = explicit_addresses;
|
||||
} else {
|
||||
ret = krb5_get_all_server_addrs(context, &addrs);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_get_all_server_addrs");
|
||||
}
|
||||
n = addrs.len;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
{
|
||||
char **names = NULL;
|
||||
int sd_n, j;
|
||||
|
||||
sockets = malloc(n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (i = 0; i < n; ++i) {
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
sd_n = sd_listen_fds_with_names(0, &names);
|
||||
if (sd_n < 0)
|
||||
krb5_err(context, 1, -sd_n, "sd_listen_fds_with_names");
|
||||
if (sd_n > 0) {
|
||||
sd_activated = 1;
|
||||
sockets = malloc(sd_n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
memset(&addrs, 0, sizeof(addrs));
|
||||
addrs.val = malloc(sd_n * sizeof(*addrs.val));
|
||||
if (addrs.val == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
n = 0;
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (j = 0; j < sd_n; j++) {
|
||||
int fd = SD_LISTEN_FDS_START + j;
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
|
||||
krb5_addr2sockaddr(context, &addrs.val[i], sa, &sa_size, port);
|
||||
|
||||
sockets[i] = socket(__ss.ss_family, SOCK_DGRAM, 0);
|
||||
if (sockets[i] < 0)
|
||||
krb5_err(context, 1, errno, "socket");
|
||||
if (bind(sockets[i], sa, sa_size) < 0) {
|
||||
char str[128];
|
||||
size_t len;
|
||||
int save_errno = errno;
|
||||
|
||||
ret = krb5_print_address(&addrs.val[i], str, sizeof(str), &len);
|
||||
if (ret)
|
||||
strlcpy(str, "unknown address", sizeof(str));
|
||||
krb5_warn(context, save_errno, "bind(%s)", str);
|
||||
continue;
|
||||
if (names == NULL || names[j] == NULL ||
|
||||
strncmp(names[j], "kpasswdd", 8) != 0)
|
||||
continue;
|
||||
if (getsockname(fd, sa, &sa_size) < 0) {
|
||||
krb5_warn(context, errno,
|
||||
"getsockname on socket-activated fd %d", fd);
|
||||
continue;
|
||||
}
|
||||
if (krb5_sockaddr2address(context, sa, &addrs.val[n]) != 0)
|
||||
continue;
|
||||
sockets[n] = fd;
|
||||
maxfd = max(maxfd, fd);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(fd, &real_fdset);
|
||||
n++;
|
||||
}
|
||||
addrs.len = n;
|
||||
}
|
||||
maxfd = max(maxfd, sockets[i]);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(sockets[i], &real_fdset);
|
||||
for (j = 0; names != NULL && names[j] != NULL; j++)
|
||||
free(names[j]);
|
||||
free(names);
|
||||
}
|
||||
#endif /* HAVE_SYSTEMD */
|
||||
|
||||
if (!sd_activated) {
|
||||
if (explicit_addresses.len) {
|
||||
addrs = explicit_addresses;
|
||||
} else {
|
||||
ret = krb5_get_all_server_addrs(context, &addrs);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_get_all_server_addrs");
|
||||
}
|
||||
n = addrs.len;
|
||||
|
||||
sockets = malloc(n * sizeof(*sockets));
|
||||
if (sockets == NULL)
|
||||
krb5_errx(context, 1, "out of memory");
|
||||
maxfd = -1;
|
||||
FD_ZERO(&real_fdset);
|
||||
for (i = 0; i < n; ++i) {
|
||||
krb5_socklen_t sa_size = sizeof(__ss);
|
||||
|
||||
krb5_addr2sockaddr(context, &addrs.val[i], sa, &sa_size, port);
|
||||
|
||||
sockets[i] = socket(__ss.ss_family, SOCK_DGRAM, 0);
|
||||
if (sockets[i] < 0)
|
||||
krb5_err(context, 1, errno, "socket");
|
||||
if (bind(sockets[i], sa, sa_size) < 0) {
|
||||
char str[128];
|
||||
size_t len;
|
||||
int save_errno = errno;
|
||||
|
||||
ret = krb5_print_address(&addrs.val[i], str, sizeof(str), &len);
|
||||
if (ret)
|
||||
strlcpy(str, "unknown address", sizeof(str));
|
||||
krb5_warn(context, save_errno, "bind(%s)", str);
|
||||
continue;
|
||||
}
|
||||
maxfd = max(maxfd, sockets[i]);
|
||||
if (maxfd >= FD_SETSIZE)
|
||||
krb5_errx(context, 1, "fd too large");
|
||||
FD_SET(sockets[i], &real_fdset);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxfd == -1)
|
||||
krb5_errx(context, 1, "No sockets!");
|
||||
|
||||
roken_detach_finish(NULL, daemon_child);
|
||||
|
||||
while (exit_flag == 0) {
|
||||
krb5_ssize_t retx;
|
||||
fd_set fdset = real_fdset;
|
||||
{
|
||||
struct timeval *tmoutp = NULL;
|
||||
#ifdef HAVE_SYSTEMD
|
||||
unsigned long nrequests = 0;
|
||||
struct timeval tmout;
|
||||
|
||||
retx = select(maxfd + 1, &fdset, NULL, NULL, NULL);
|
||||
if (retx < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
krb5_err(context, 1, errno, "select");
|
||||
}
|
||||
for (i = 0; i < n; ++i)
|
||||
if (FD_ISSET(sockets[i], &fdset)) {
|
||||
u_char buf[BUFSIZ];
|
||||
socklen_t addrlen = sizeof(__ss);
|
||||
tmoutp = &tmout;
|
||||
sd_notify(0, "READY=1");
|
||||
sd_notifyf(0, "STATUS=Serving; %lu password change request(s) processed",
|
||||
nrequests);
|
||||
#endif
|
||||
|
||||
retx = recvfrom(sockets[i], buf, sizeof(buf), 0,
|
||||
sa, &addrlen);
|
||||
if (retx < 0) {
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
else
|
||||
krb5_err(context, 1, errno, "recvfrom");
|
||||
}
|
||||
while (exit_flag == 0) {
|
||||
krb5_ssize_t retx;
|
||||
fd_set fdset = real_fdset;
|
||||
|
||||
process(keytab, sockets[i],
|
||||
&addrs.val[i],
|
||||
sa, addrlen,
|
||||
buf, retx);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
tmout.tv_sec = STATUS_INTERVAL;
|
||||
tmout.tv_usec = 0;
|
||||
#endif
|
||||
retx = select(maxfd + 1, &fdset, NULL, NULL, tmoutp);
|
||||
if (retx < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
krb5_err(context, 1, errno, "select");
|
||||
}
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (retx == 0) {
|
||||
/* select timed out: refresh our systemd status line */
|
||||
sd_notifyf(0, "STATUS=Serving; %lu password change request(s) "
|
||||
"processed", nrequests);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
for (i = 0; i < n; ++i)
|
||||
if (FD_ISSET(sockets[i], &fdset)) {
|
||||
u_char buf[BUFSIZ];
|
||||
socklen_t addrlen = sizeof(__ss);
|
||||
|
||||
retx = recvfrom(sockets[i], buf, sizeof(buf), 0,
|
||||
sa, &addrlen);
|
||||
if (retx < 0) {
|
||||
if (errno == EINTR)
|
||||
break;
|
||||
else
|
||||
krb5_err(context, 1, errno, "recvfrom");
|
||||
}
|
||||
|
||||
process(keytab, sockets[i],
|
||||
&addrs.val[i],
|
||||
sa, addrlen,
|
||||
buf, retx);
|
||||
#ifdef HAVE_SYSTEMD
|
||||
nrequests++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
sd_notify(0, "STOPPING=1");
|
||||
#endif
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
close(sockets[i]);
|
||||
free(sockets);
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
{
|
||||
src,
|
||||
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
autoreconfHook,
|
||||
pkg-config,
|
||||
python3,
|
||||
perl,
|
||||
bison,
|
||||
flex,
|
||||
texinfo,
|
||||
perlPackages,
|
||||
|
||||
openldap,
|
||||
libcap_ng,
|
||||
sqlite,
|
||||
openssl,
|
||||
db,
|
||||
libedit,
|
||||
pam,
|
||||
libmicrohttpd,
|
||||
cjson,
|
||||
systemdLibs,
|
||||
|
||||
CoreFoundation,
|
||||
Security,
|
||||
SystemConfiguration,
|
||||
|
||||
curl,
|
||||
jdk_headless,
|
||||
unzip,
|
||||
which,
|
||||
|
||||
nixosTests,
|
||||
|
||||
withCJSON ? true,
|
||||
withCapNG ? stdenv.hostPlatform.isLinux,
|
||||
# libmicrohttpd should theoretically work for darwin as well, but something is broken.
|
||||
# It affects tests check-bx509d and check-httpkadmind.
|
||||
withMicroHTTPD ? stdenv.hostPlatform.isLinux,
|
||||
withOpenLDAP ? true,
|
||||
withOpenLDAPAsHDBModule ? false,
|
||||
withOpenSSL ? true,
|
||||
withSQLite3 ? true,
|
||||
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemdLibs,
|
||||
}:
|
||||
|
||||
assert lib.assertMsg (withOpenLDAPAsHDBModule -> withOpenLDAP) ''
|
||||
OpenLDAP needs to be enabled in order to build the OpenLDAP HDB Module.
|
||||
'';
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "heimdal";
|
||||
version = "7.8.0-unstable-local";
|
||||
|
||||
inherit src;
|
||||
|
||||
outputs = [
|
||||
"out"
|
||||
"dev"
|
||||
"man"
|
||||
"info"
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoreconfHook
|
||||
pkg-config
|
||||
python3
|
||||
perl
|
||||
bison
|
||||
flex
|
||||
perlPackages.JSON
|
||||
texinfo
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
[
|
||||
db
|
||||
libedit
|
||||
pam
|
||||
]
|
||||
++ lib.optionals (stdenv.hostPlatform.isDarwin) [
|
||||
CoreFoundation
|
||||
Security
|
||||
SystemConfiguration
|
||||
]
|
||||
++ lib.optionals (withCJSON) [ cjson ]
|
||||
++ lib.optionals (withCapNG) [ libcap_ng ]
|
||||
++ lib.optionals (withMicroHTTPD) [ libmicrohttpd ]
|
||||
++ lib.optionals (withOpenLDAP) [ openldap ]
|
||||
++ lib.optionals (withOpenSSL) [ openssl ]
|
||||
++ lib.optionals (withSQLite3) [ sqlite ]
|
||||
++ lib.optionals (withSystemd) [ systemdLibs ];
|
||||
|
||||
doCheck = true;
|
||||
nativeCheckInputs = [
|
||||
curl
|
||||
jdk_headless
|
||||
unzip
|
||||
which
|
||||
];
|
||||
|
||||
configureFlags =
|
||||
[
|
||||
"--with-hdbdir=/var/lib/heimdal"
|
||||
|
||||
"--with-libedit-include=${libedit.dev}/include"
|
||||
"--with-libedit-lib=${libedit}/lib"
|
||||
"--with-berkeley-db-include=${db.dev}/include"
|
||||
"--with-berkeley-db"
|
||||
|
||||
"--without-x"
|
||||
"--disable-afs-string-to-key"
|
||||
]
|
||||
++ lib.optionals (withCapNG) [
|
||||
"--with-capng"
|
||||
]
|
||||
++ lib.optionals (withCJSON) [
|
||||
"--with-cjson=${cjson}"
|
||||
]
|
||||
++ lib.optionals (withOpenLDAP) [
|
||||
"--with-openldap=${openldap.dev}"
|
||||
]
|
||||
++ lib.optionals (withOpenLDAPAsHDBModule) [
|
||||
"--enable-hdb-openldap-module"
|
||||
]
|
||||
++ lib.optionals (withSQLite3) [
|
||||
"--with-sqlite3=${sqlite.dev}"
|
||||
]
|
||||
++ lib.optionals (withSystemd) [
|
||||
"--with-systemd"
|
||||
];
|
||||
|
||||
# (check-ldap) slapd resides within ${openldap}/libexec,
|
||||
# which is not part of $PATH by default.
|
||||
# (check-ldap) prepending ${openldap}/bin to the path to avoid
|
||||
# using the default installation of openldap on unsandboxed darwin systems,
|
||||
# which does not support the new mdb backend at the moment (2024-01-13).
|
||||
postPatch = ''
|
||||
substituteInPlace tests/ldap/slapd-init.in \
|
||||
--replace-fail 'SCHEMA_PATHS="' 'SCHEMA_PATHS="${openldap}/etc/schema '
|
||||
substituteInPlace tests/ldap/check-ldap.in \
|
||||
--replace-fail 'PATH=' 'PATH=${openldap}/libexec:${openldap}/bin:'
|
||||
substituteInPlace tests/ldap/Makefile.am \
|
||||
--replace-fail 'TESTS = check-ldap' 'TESTS ='
|
||||
substituteInPlace tests/kdc/check-iprop.in \
|
||||
--replace-fail '/bin/pwd' 'pwd'
|
||||
'';
|
||||
|
||||
# (test_cc) heimdal uses librokens implementation of `secure_getenv` on darwin,
|
||||
# which expects either USER or LOGNAME to be set.
|
||||
preCheck = lib.optionalString (stdenv.hostPlatform.isDarwin) ''
|
||||
export USER=nix-builder
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $dev/bin
|
||||
mv $out/bin/krb5-config $dev/bin/
|
||||
|
||||
# asn1 compilers, move them to $dev
|
||||
mv $out/libexec/heimdal/* $dev/bin
|
||||
rmdir $out/libexec/heimdal
|
||||
|
||||
# compile_et is needed for cross-compiling this package and samba
|
||||
mv lib/com_err/.libs/compile_et $dev/bin
|
||||
'';
|
||||
|
||||
# Issues with hydra
|
||||
# In file included from hxtool.c:34:0:
|
||||
# hx_locl.h:67:25: fatal error: pkcs10_asn1.h: No such file or directory
|
||||
#enableParallelBuilding = true;
|
||||
|
||||
passthru = {
|
||||
implementation = "heimdal";
|
||||
tests.nixos = nixosTests.kerberos.heimdal;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
homepage = "https://www.heimdal.software";
|
||||
changelog = "https://github.com/heimdal/heimdal/releases";
|
||||
description = "Implementation of Kerberos 5 (and some more stuff)";
|
||||
license = licenses.bsd3;
|
||||
platforms = platforms.unix;
|
||||
maintainers = with maintainers; [ h7x4 ];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
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";
|
||||
|
||||
enableSocketActivation = lib.mkEnableOption ''
|
||||
Systemd socket activation for the kerberos daemons.
|
||||
'';
|
||||
|
||||
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 = lib.mkIf (!cfg.enableSocketActivation) [ "multi-user.target" ];
|
||||
};
|
||||
};
|
||||
|
||||
# meta = {
|
||||
# doc = ./kerberos-server.md;
|
||||
# };
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (lib) mapAttrs;
|
||||
cfg = config.services.kerberos_server;
|
||||
package = config.security.krb5.package;
|
||||
|
||||
socketActivation = cfg.enableSocketActivation;
|
||||
|
||||
serviceWantedBy = [ "kerberos-server.target" ];
|
||||
|
||||
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 = serviceWantedBy;
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kadmind --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
|
||||
PrivateNetwork = socketActivation;
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
|
||||
systemd.services.kdc = {
|
||||
description = "Key Distribution Center daemon";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = serviceWantedBy;
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kdc --config-file=/etc/heimdal-kdc/kdc.conf";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
PrivateNetwork = socketActivation;
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
|
||||
systemd.services.kpasswdd = {
|
||||
description = "Kerberos Password Changing daemon";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = serviceWantedBy;
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
ExecStart = "${package}/libexec/kpasswdd";
|
||||
Slice = "system-kerberos-server.slice";
|
||||
StateDirectory = "heimdal";
|
||||
PrivateNetwork = socketActivation;
|
||||
};
|
||||
restartTriggers = [ kdcConfFile ];
|
||||
};
|
||||
|
||||
systemd.sockets = lib.mkIf socketActivation {
|
||||
kadmind = {
|
||||
description = "Kerberos Administration Daemon socket";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [
|
||||
"sockets.target"
|
||||
"kerberos-server.target"
|
||||
];
|
||||
socketConfig = {
|
||||
ListenStream = 749;
|
||||
FileDescriptorName = "kadmind";
|
||||
Accept = false;
|
||||
Slice = "system-kerberos-server.slice";
|
||||
};
|
||||
};
|
||||
|
||||
kdc = {
|
||||
description = "Key Distribution Center daemon socket";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [
|
||||
"sockets.target"
|
||||
"kerberos-server.target"
|
||||
];
|
||||
socketConfig = {
|
||||
ListenStream = 88;
|
||||
ListenDatagram = 88;
|
||||
FileDescriptorName = "kdc";
|
||||
Accept = false;
|
||||
Slice = "system-kerberos-server.slice";
|
||||
};
|
||||
};
|
||||
|
||||
kpasswdd = {
|
||||
description = "Kerberos Password Changing daemon socket";
|
||||
partOf = [ "kerberos-server.target" ];
|
||||
wantedBy = [
|
||||
"sockets.target"
|
||||
"kerberos-server.target"
|
||||
];
|
||||
socketConfig = {
|
||||
ListenDatagram = 464;
|
||||
FileDescriptorName = "kpasswdd";
|
||||
Accept = false;
|
||||
Slice = "system-kerberos-server.slice";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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}
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
{ nixpkgs }:
|
||||
(
|
||||
{ pkgs, lib, ... }:
|
||||
{
|
||||
name = "kerberos_server-heimdal";
|
||||
|
||||
nodes = {
|
||||
server =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||
imports = [
|
||||
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||
./module
|
||||
];
|
||||
|
||||
users.users.alice.extraGroups = [ "wheel" ];
|
||||
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
|
||||
time.timeZone = "Etc/UTC";
|
||||
|
||||
networking = {
|
||||
domain = "foo.bar";
|
||||
useDHCP = false;
|
||||
firewall.enable = false;
|
||||
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.1/24";
|
||||
};
|
||||
|
||||
security.krb5 = {
|
||||
enable = true;
|
||||
package = pkgs.heimdal;
|
||||
settings = {
|
||||
libdefaults.default_realm = "FOO.BAR";
|
||||
|
||||
# Enable extra debug output
|
||||
logging = {
|
||||
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||
default = "SYSLOG:DEBUG:AUTH";
|
||||
kdc = "SYSLOG:DEBUG:AUTH";
|
||||
};
|
||||
|
||||
realms = {
|
||||
"FOO.BAR" = {
|
||||
admin_server = "server.foo.bar";
|
||||
kpasswd_server = "server.foo.bar";
|
||||
kdc = [ "server.foo.bar" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.kerberos_server = {
|
||||
enable = true;
|
||||
settings.realms = {
|
||||
"FOO.BAR" = {
|
||||
acl = [
|
||||
{
|
||||
principal = "kadmin/admin@FOO.BAR";
|
||||
access = "all";
|
||||
target = "";
|
||||
}
|
||||
{
|
||||
principal = "alice/admin@FOO.BAR";
|
||||
access = [
|
||||
"add"
|
||||
"cpw"
|
||||
"delete"
|
||||
"get"
|
||||
"list"
|
||||
"modify"
|
||||
];
|
||||
target = "";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
specialisation.socketActivation.configuration = {
|
||||
services.kerberos_server.enableSocketActivation = true;
|
||||
};
|
||||
};
|
||||
|
||||
client =
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
disabledModules = [ "services/system/kerberos/default.nix" ];
|
||||
imports = [
|
||||
"${nixpkgs}/nixos/tests/common/user-account.nix"
|
||||
./module
|
||||
];
|
||||
|
||||
users.users.alice.extraGroups = [ "wheel" ];
|
||||
|
||||
services.getty.autologinUser = "alice";
|
||||
|
||||
virtualisation.vlans = [ 1 ];
|
||||
|
||||
time.timeZone = "Etc/UTC";
|
||||
|
||||
networking = {
|
||||
domain = "foo.bar";
|
||||
useDHCP = false;
|
||||
hosts."10.0.0.1" = [ "server.foo.bar" ];
|
||||
hosts."10.0.0.2" = [ "client.foo.bar" ];
|
||||
};
|
||||
|
||||
systemd.network.networks."01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig.Address = "10.0.0.2/24";
|
||||
};
|
||||
|
||||
security.krb5 = {
|
||||
enable = true;
|
||||
package = pkgs.heimdal;
|
||||
settings = {
|
||||
libdefaults.default_realm = "FOO.BAR";
|
||||
|
||||
logging = {
|
||||
admin_server = "SYSLOG:DEBUG:AUTH";
|
||||
default = "SYSLOG:DEBUG:AUTH";
|
||||
kdc = "SYSLOG:DEBUG:AUTH";
|
||||
};
|
||||
|
||||
realms = {
|
||||
"FOO.BAR" = {
|
||||
admin_server = "server.foo.bar";
|
||||
kpasswd_server = "server.foo.bar";
|
||||
kdc = [ "server.foo.bar" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript =
|
||||
{ nodes, ... }:
|
||||
let
|
||||
expectTemplate = program: argc: interaction:
|
||||
pkgs.writeScriptBin "${program}-auto-password" ''
|
||||
#!${pkgs.expect}/bin/expect -f
|
||||
|
||||
set timeout 30
|
||||
${lib.concatMapStringsSep "\n" (i: "set arg${toString i} [lindex $argv ${toString i}]") (lib.range 0 (argc - 1))}
|
||||
set args [lrange $argv ${toString argc} end]
|
||||
eval spawn ${program} $args
|
||||
|
||||
${interaction}
|
||||
|
||||
expect eof
|
||||
set exit_status [lindex [wait] 3]
|
||||
exit $exit_status
|
||||
'';
|
||||
|
||||
kadmin = expectTemplate "kadmin" 1 ''
|
||||
expect {
|
||||
"alice/admin@FOO.BAR's Password: " {
|
||||
send -- "$arg0\n"
|
||||
}
|
||||
timeout {
|
||||
puts stderr "Error: Timeout waiting for password prompt"
|
||||
exit 2
|
||||
}
|
||||
eof {
|
||||
puts stderr "Error: kadmin exited unexpectedly"
|
||||
break
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
ktutil = expectTemplate "ktutil" 1 ''
|
||||
expect {
|
||||
"alice/admin@FOO.BAR's Password: " {
|
||||
send -- "$arg0\n"
|
||||
}
|
||||
timeout {
|
||||
puts stderr "Error: Timeout waiting for password prompt"
|
||||
exit 2
|
||||
}
|
||||
eof {
|
||||
puts stderr "Error: ktutil exited unexpectedly"
|
||||
break
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
kpasswd = expectTemplate "kpasswd" 2 ''
|
||||
set exchanges [list \
|
||||
[list "alice@FOO.BAR's Password: " $arg0] \
|
||||
[list "New password: " $arg1] \
|
||||
[list "Verify password - New password: " $arg1] \
|
||||
]
|
||||
|
||||
foreach pair $exchanges {
|
||||
lassign $pair prompt reply
|
||||
|
||||
expect {
|
||||
-exact $prompt {
|
||||
send -- "$reply\n"
|
||||
}
|
||||
timeout {
|
||||
puts stderr "Error: Timeout waiting for: $prompt"
|
||||
exit 2
|
||||
}
|
||||
eof {
|
||||
puts stderr "Error: Unexpected EOF while waiting for: $prompt"
|
||||
exit 3
|
||||
}
|
||||
}
|
||||
}
|
||||
'';
|
||||
in
|
||||
''
|
||||
import string
|
||||
import random
|
||||
random.seed(0)
|
||||
|
||||
start_all()
|
||||
|
||||
with subtest("Server: initialize realm"):
|
||||
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||
server.wait_for_unit(unit)
|
||||
|
||||
server.succeed("kadmin -l init --realm-max-ticket-life='8 day' --realm-max-renewable-life='10 day' FOO.BAR")
|
||||
|
||||
for unit in ["kadmind.service", "kdc.service", "kpasswdd.service"]:
|
||||
server.systemctl(f"restart {unit}")
|
||||
|
||||
alice_krb_pw = "alice_hunter2"
|
||||
alice_old_krb_pw = ""
|
||||
alice_krb_admin_pw = "alice_admin_hunter2"
|
||||
bob_krb_pw = "bob_hunter2"
|
||||
|
||||
def random_password():
|
||||
password_chars = string.ascii_letters + string.digits + "-_"
|
||||
return "".join(random.choice(password_chars) for _ in range(16))
|
||||
|
||||
def kinit(node, user, password):
|
||||
node.succeed(
|
||||
f"echo '{password}' > /tmp/pw.txt",
|
||||
f"kinit --password-file=/tmp/pw.txt {user}",
|
||||
"rm /tmp/pw.txt",
|
||||
)
|
||||
tickets = node.succeed("klist")
|
||||
assert f"Principal: {user}@FOO.BAR" in tickets
|
||||
|
||||
def kadmin(node, command, localAuth=False):
|
||||
if localAuth:
|
||||
return node.succeed(f"kadmin -l {command}")
|
||||
else:
|
||||
return node.succeed(f"${lib.getExe kadmin} '{alice_krb_admin_pw}' -p alice/admin {command}")
|
||||
|
||||
with subtest("Server: initialize user principals and keytabs"):
|
||||
kadmin(server, f'add --password="{alice_krb_admin_pw}" --use-defaults alice/admin', localAuth=True)
|
||||
kadmin(server, f'add --password="{alice_krb_pw}" --use-defaults alice')
|
||||
kadmin(server, f'add --password="{bob_krb_pw}" --use-defaults bob')
|
||||
kadmin(server, 'check')
|
||||
|
||||
server.wait_for_unit("getty@tty1.service")
|
||||
server.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
server.wait_for_unit("default.target")
|
||||
|
||||
with subtest("Server: initialize host principal with keytab"):
|
||||
server.succeed(f"${lib.getExe ktutil} '{alice_krb_admin_pw}' get -p alice/admin host/server.foo.bar")
|
||||
server.wait_for_file("/etc/krb5.keytab")
|
||||
|
||||
ktutil_list = server.succeed("ktutil list")
|
||||
if not "host/server.foo.bar" in ktutil_list:
|
||||
exit(1)
|
||||
|
||||
client.systemctl("start network-online.target")
|
||||
client.wait_for_unit("network-online.target")
|
||||
client.wait_for_unit("getty@tty1.service")
|
||||
client.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
client.wait_for_unit("default.target")
|
||||
|
||||
with subtest("Client: initialize host principal with keytab"):
|
||||
kinit(client, "alice/admin", alice_krb_admin_pw)
|
||||
client.succeed(f"${lib.getExe ktutil} '{alice_krb_admin_pw}' get -p alice/admin host/client.foo.bar")
|
||||
client.wait_for_file("/etc/krb5.keytab")
|
||||
|
||||
ktutil_list = client.succeed("ktutil list")
|
||||
if not "host/client.foo.bar" in ktutil_list:
|
||||
exit(1)
|
||||
|
||||
def kerberos_auth_flow(mode):
|
||||
global alice_krb_pw, alice_old_krb_pw
|
||||
|
||||
with subtest(f"[{mode}] Client: kinit alice"):
|
||||
kinit(client, "alice", alice_krb_pw)
|
||||
|
||||
with subtest(f"[{mode}] Client: kpasswd alice"):
|
||||
alice_old_krb_pw = alice_krb_pw
|
||||
alice_krb_pw = random_password()
|
||||
output = client.succeed(f"${lib.getExe kpasswd} {alice_old_krb_pw} {alice_krb_pw}")
|
||||
assert "Success : Password changed" in output
|
||||
|
||||
with subtest(f"[{mode}] Client: kadmin get bob"):
|
||||
output = kadmin(client, "get bob")
|
||||
assert "Principal: bob@FOO.BAR" in output
|
||||
|
||||
with subtest(f"[{mode}] Server: kinit alice"):
|
||||
kinit(server, "alice", alice_krb_pw)
|
||||
|
||||
with subtest(f"[{mode}] Server: kpasswd alice"):
|
||||
alice_old_krb_pw = alice_krb_pw
|
||||
alice_krb_pw = random_password()
|
||||
output = server.succeed(f"${lib.getExe kpasswd} {alice_old_krb_pw} {alice_krb_pw}")
|
||||
assert "Success : Password changed" in output
|
||||
|
||||
with subtest(f"[{mode}] Server: kadmin get bob"):
|
||||
output = kadmin(server, "get bob")
|
||||
assert "Principal: bob@FOO.BAR" in output
|
||||
|
||||
# First run against the default, self-binding daemons.
|
||||
kerberos_auth_flow("bind")
|
||||
|
||||
with subtest("Server: daemons publish an sd_notify status line"):
|
||||
for svc in ["kdc", "kadmind", "kpasswdd"]:
|
||||
status = server.succeed(f"systemctl show {svc}.service -p StatusText")
|
||||
assert "Serving" in status, \
|
||||
f"{svc}.service has no sd_notify status: {status}"
|
||||
|
||||
server.succeed("systemctl stop kadmind.service kdc.service kpasswdd.service")
|
||||
server.succeed(
|
||||
"/run/current-system/specialisation/socketActivation/bin/switch-to-configuration test"
|
||||
)
|
||||
for unit in ["kadmind.socket", "kdc.socket", "kpasswdd.socket"]:
|
||||
server.wait_for_unit(unit)
|
||||
|
||||
kerberos_auth_flow("socket-activation")
|
||||
|
||||
with subtest("Server: daemons are wired to their activation sockets"):
|
||||
for svc in ["kdc", "kadmind", "kpasswdd"]:
|
||||
triggered = server.succeed(f"systemctl show {svc}.service -p TriggeredBy")
|
||||
assert f"{svc}.socket" in triggered, \
|
||||
f"{svc}.service not triggered by {svc}.socket: {triggered}"
|
||||
'';
|
||||
|
||||
meta.maintainers = pkgs.heimdal.meta.maintainers;
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
{ pkgs, lib }:
|
||||
pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
autoconf
|
||||
automake
|
||||
autogen
|
||||
autoreconfHook
|
||||
|
||||
pkg-config
|
||||
python3
|
||||
perl
|
||||
bison
|
||||
flex
|
||||
perlPackages.JSON
|
||||
texinfo
|
||||
|
||||
# check inputs
|
||||
curl
|
||||
jdk_headless
|
||||
unzip
|
||||
which
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
db
|
||||
libedit
|
||||
pam
|
||||
cjson
|
||||
libcap_ng
|
||||
libmicrohttpd
|
||||
openldap
|
||||
openssl
|
||||
sqlite
|
||||
systemd
|
||||
];
|
||||
|
||||
env = {
|
||||
CFLAGS = lib.concatStringsSep " " [
|
||||
# From github workflows
|
||||
"-Wno-error=shadow"
|
||||
"-Wno-error=bad-function-cast"
|
||||
"-Wno-error=unused-function"
|
||||
"-Wno-error=unused-result"
|
||||
"-Wno-error=deprecated-declarations"
|
||||
|
||||
# idk, but it complained about these during compilation.
|
||||
# maybe they come from the nix environment, or too new compiler?
|
||||
"-Wno-error=maybe-uninitialized"
|
||||
"-Wno-error=format-overflow"
|
||||
];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user