9 Commits

Author SHA1 Message Date
oysteikt 58b4b1f06e nix: fix package, test, and module with systemd integration 2026-07-22 15:56:38 +09:00
oysteikt 583408b3e3 nix/nixosTest: fix 2026-07-22 15:26:23 +09:00
oysteikt f8cc75b9c6 Set up nix tooling 2026-07-22 15:26:22 +09:00
oysteikt 69e74be224 kadmind: add support for systemd socket activation
Adds support for systemd's fd-passing socket activation, letting systemd
listen for connections before starting the service. This might enable
quicker startup on systems with dependent services, since they will now
only need to wait for the socket, which holds connections until kdc is
finished starting up. It also enables use of the `PrivateNetwork`
directive, sandboxing kadmind into it's own network namespace.
2026-07-22 15:14:09 +09:00
oysteikt f6ec5e5f9a kpasswd: add support for systemd socket activation
Adds support for systemd's fd-passing socket activation, letting systemd
listen for connections before starting the service. This might enable
quicker startup on systems with dependent services, since they will now
only need to wait for the socket, which holds connections until kdc is
finished starting up. It also enables use of the `PrivateNetwork`
directive, sandboxing kpasswd into it's own network namespace.
2026-07-22 15:14:09 +09:00
oysteikt 049748c182 kdc: add support for systemd socket activation
Adds support for systemd's fd-passing socket activation, letting systemd
listen for connections before starting the service. This might enable
quicker startup on systems with dependent services, since they will now
only need to wait for the socket, which holds connections until kdc is
finished starting up. It also enables use of the `PrivateNetwork`
directive, sandboxing KDC into it's own network namespace.
2026-07-22 15:14:08 +09:00
oysteikt 85b816b5bc kadmind: add sd_notify start/stop signals
This commit adds optional integration with systemd's state
notifification system.

When built with libsystemd, kadmind notifies systemd once it is
ready to serve and when it begins a clean shutdown, as well as
publishing a status line reporting the number of accepted connections.
2026-07-22 15:10:16 +09:00
oysteikt b795025613 kpasswd: add sd_notify start/stop signals
This commit adds optional integration with systemd's state
notifification system.

When built with libsystemd, kpasswdd notifies systemd once it is
ready to serve and when it begins a clean shutdown, as well as
publishing a status line reporting how many password change requests
have been processed.
2026-07-22 15:10:15 +09:00
oysteikt 4cba7c91d1 kdc: add sd_notify start/stop signals
This commit adds optional integration with systemd's state
notifification system.

When built with libsystemd, the KDC notifies systemd once it is
ready to serve and when it begins a clean shutdown, as well as
publishing a status line reporting how many worker processes are
running.
2026-07-22 15:10:15 +09:00
15 changed files with 463 additions and 139 deletions
-8
View File
@@ -1,8 +0,0 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
{
"tab_size": 8,
"hard_tabs": false
}
+16
View File
@@ -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]),
-1
View File
@@ -28,7 +28,6 @@
fileset = lib.fileset.difference ./. (lib.fileset.unions [
./.github
./.gitignore
./.zed
./flake.nix
./flake.lock
./nix
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
@@ -1,26 +0,0 @@
From 08d719e96214f648ae95043acc308deca36e1f7a Mon Sep 17 00:00:00 2001
From: Ihar Hrachyshka <ihar.hrachyshka@gmail.com>
Date: Tue, 15 Oct 2024 13:52:39 -0400
Subject: [PATCH] Define HAVE_DB_185_H
---
cf/db.m4 | 3 +++
1 file changed, 3 insertions(+)
diff --git a/cf/db.m4 b/cf/db.m4
index c0b4510b6..c95a9dee9 100644
--- a/cf/db.m4
+++ b/cf/db.m4
@@ -57,6 +57,9 @@ AS_IF([test "x$with_berkeley_db" != xno],
db.h \
])])
+dnl detect if compat db_185.h is present
+AC_CHECK_HEADERS([db_185.h])
+
dnl db_create is used by db3 and db4 and db5 and db6
AC_FIND_FUNC_NO_LIBS(db_create, [$dbheader] db-6 db-5 db4 db3 db, [
--
2.46.0
+7 -6
View File
@@ -22,6 +22,7 @@
pam,
libmicrohttpd,
cjson,
systemdLibs,
CoreFoundation,
Security,
@@ -43,6 +44,7 @@
withOpenLDAPAsHDBModule ? false,
withOpenSSL ? true,
withSQLite3 ? true,
withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemdLibs,
}:
assert lib.assertMsg (withOpenLDAPAsHDBModule -> withOpenLDAP) ''
@@ -89,7 +91,8 @@ stdenv.mkDerivation {
++ lib.optionals (withMicroHTTPD) [ libmicrohttpd ]
++ lib.optionals (withOpenLDAP) [ openldap ]
++ lib.optionals (withOpenSSL) [ openssl ]
++ lib.optionals (withSQLite3) [ sqlite ];
++ lib.optionals (withSQLite3) [ sqlite ]
++ lib.optionals (withSystemd) [ systemdLibs ];
doCheck = true;
nativeCheckInputs = [
@@ -125,13 +128,11 @@ stdenv.mkDerivation {
]
++ lib.optionals (withSQLite3) [
"--with-sqlite3=${sqlite.dev}"
]
++ lib.optionals (withSystemd) [
"--with-systemd"
];
patches = [
# Proposed @ https://github.com/heimdal/heimdal/pull/1264
./0001-Define-HAVE_DB_185_H.patch
];
# (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
+5 -1
View File
@@ -30,6 +30,10 @@ in
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 = ''
@@ -59,7 +63,7 @@ in
systemd.slices.system-kerberos-server = { };
systemd.targets.kerberos-server = {
wantedBy = [ "multi-user.target" ];
wantedBy = lib.mkIf (!cfg.enableSocketActivation) [ "multi-user.target" ];
};
};
+62 -3
View File
@@ -10,6 +10,10 @@ let
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:
@@ -69,11 +73,14 @@ in
systemd.services.kadmind = {
description = "Kerberos Administration Daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "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 ];
};
@@ -81,11 +88,13 @@ in
systemd.services.kdc = {
description = "Key Distribution Center daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "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 ];
};
@@ -93,13 +102,63 @@ in
systemd.services.kpasswdd = {
description = "Kerberos Password Changing daemon";
partOf = [ "kerberos-server.target" ];
wantedBy = [ "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";
};
};
};
};
}
+51 -21
View File
@@ -84,6 +84,10 @@
};
};
};
specialisation.socketActivation.configuration = {
services.kerberos_server.enableSocketActivation = true;
};
};
client =
@@ -289,32 +293,58 @@
if not "host/client.foo.bar" in ktutil_list:
exit(1)
with subtest("Client: kinit alice"):
kinit(client, "alice", alice_krb_pw)
def kerberos_auth_flow(mode):
global alice_krb_pw, alice_old_krb_pw
with subtest("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: kinit alice"):
kinit(client, "alice", alice_krb_pw)
with subtest("Client: kadmin get bob"):
output = kadmin(client, "get bob")
print(output)
assert "Principal: bob@FOO.BAR" in output
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("Server: kinit alice"):
kinit(server, "alice", alice_krb_pw)
with subtest(f"[{mode}] Client: kadmin get bob"):
output = kadmin(client, "get bob")
assert "Principal: bob@FOO.BAR" in output
with subtest("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: kinit alice"):
kinit(server, "alice", alice_krb_pw)
with subtest("Server: kadmin get bob"):
output = kadmin(server, "get bob")
assert "Principal: bob@FOO.BAR" in output
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;
+1
View File
@@ -31,6 +31,7 @@ pkgs.mkShell {
openldap
openssl
sqlite
systemd
];
env = {