0

I set up a FreeRADIUS server with a SQL backend running on a remote MariaDB instance. Authentication and traffic encryption should be handled by Kerberos using k5start to maintain the tickets. The system runs on a Debian 11 instance.

I created an override for /etc/systemd/system/freeradius.service:

[Service]
Environment="KRB5CCNAME=/run/freeradius/sql.tkt"
ExecStartPre=/usr/bin/k5start -u radius_user -f /etc/freeradius/3.0/freeradius.keytab -o freerad -g freerad -m 600 -H 60 -b -K 30 -L -k /run/freeradius/sql.tkt

This basically works. However, since this is a background service it may well happen that k5start on one evil day crashes and then after the ticket expired RADIUS will be down.

Another issue of lesser concern is that k5start in this case is run as the RADIUS user i.e., the keytab must be readable by the RADIUS user. I like it better to have it as root:root 0600.

Of course I could run k5start as a service of its own, which has the minor beauty issue that /run/freeradius does not necessarily exist, when k5start is launched i.e., I have to use yet another directory for the tickets and given PrivateTmp=true its probably not in /tmp.

So, for my ideal solution the freeradius.service would create the /run/freeradius directory, then launch something like a ticket.service and then resume to start RADIUS. Looks similar to BindsTo=, but still somehow different.

Is there a nicer solution to have systemd monitor k5start and restart it in case it is gone?

Lars Hanke
  • 285
  • 3
  • 16

1 Answers1

1

Start multiple dependent daemons in one systemd service

Don't. Systemd explicitly does not support more than one "monitored" process per .service unit. The k5start should be a separate service that FreeRADIUS depends on.

So, for my ideal solution the freeradius.service would create the /run/freeradius directory, then launch something like a ticket.service and then resume to start RADIUS. Looks similar to BindsTo=, but still somehow different.

Pre-create the directory via tmpfiles.d, as was common practice before the "RuntimeDirectory=" was added to systemd as a feature.

/etc/tmpfiles.d/freeradius.conf
d /run/freeradius 0750 radius radius -

Once you have that, you can link the two services using regular Requires= and After= dependencies:

[Unit]
Requires=kstart@radius.service
After=kstart@radius.service

Note that with recent MIT libkrb5, you don't need a separate task to maintain the keytab – libkrb5 can acquire tickets using a "client keytab" all on its own, if specified via environment variable:

radiusd.conf
ENV { KRB5_CLIENT_KTNAME = 'FILE:/etc/raddb/radius.keytab' }

...or if the keytab is placed at FILE:/var/lib/krb5/user/%{euid}/client.keytab (see default_client_keytab_name in krb5.conf(5).)

Another issue of lesser concern is that k5start in this case is run as the RADIUS user i.e., the keytab must be readable by the RADIUS user. I like it better to have it as root:root 0600.

k5start supports chown'ing the ticket cache to another user using the -o <user> option.

An alternative option is to use gssproxy, which is like ssh-agent for Kerberos tickets. With gssproxy enabled, radiusd will not need a ticket cache at all – gssproxy will be the one to renew tickets and establish GSS security contexts on behalf of radiusd.

/etc/gssproxy/50-radius.conf
[service/ldap] mechs = krb5 cred_store = ccache:/var/lib/gssproxy/clients/krb5cc_%U cred_store = client_keytab:/etc/radius.keytab cred_usage = initiate euid = radius_user
radiusd.conf
ENV { GSS_USE_PROXY = '1' }
user1686
  • 10,162
  • 1
  • 26
  • 42
  • Thanks for this detailed answer. I tried the favorite solution using KRB5_CLIENT_KTNAME, but it does not seem to work. KRB5_TRACE does not produce a single output then. The kstart@ idea was also promising, but it seems to only work, if all paths and principals are quite standardized. So, I used the gssproxy method, and it works like a charm, once you find out, where to look for logs ... – Lars Hanke Jun 19 '23 at 19:15
  • If the paths and principals aren't standardized, you can just use kstart@this and kstart@that as completely independent units, with each having the correct paths hardcoded (or maybe set via `Environment=`) – or really you can just call it `kstart-for-radius.service`. – user1686 Jun 19 '23 at 19:20