/etc/systemd/timesyncd sollte denselben NTP-Server haben, am besten einen lokalen
root@linux:~# timedatectl
Local time: Tue 2025-01-01 02:01:01 CET
Universal time: Tue 2025-01-01 01:01:01 UTC
RTC time: Tue 2025-01-01 01:01:01
Time zone: Europe/Berlin (CET, +0100)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
_kerberos.domain.tld TXT "DOMAIN.TLD"
_kerberos._tcp.domain.tld. IN SRV 1 0 88 auth.domain.tld.
_kerberos-adm._tcp.domain.tld. IN SRV 1 0 749 auth.domain.tld.
_kpasswd._tcp.domain.tld. IN SRV 1 0 464 auth.domain.tld.
Kerberos scheint nicht auf UDP zu lauschen, ich setze daher nur _tcp.
Wer OpenWRT hat und die TXT-Einstellungen sucht die ab 2024 auf wundersame Weise in Luci verschwunden sind: Die TXT-Records kann man in /etc/dnsmasq.conf setzen:
txt-record=_kerberos.domain.tld,"DOMAIN.TLD"
Wir brauchen diese Records zwar nicht zwingend, aber die Client-Config ist so schlanker und der Laptop z.B. auch in anderen Domänen ohne Angabe von @DOMAIN.TLD daheim.
Sollte der Server automatisch haben
root@client:~# apt install krb5-user
REALM ist DOMAIN.TLD, server ist kdc und admin: auth.domain.tld
/etc/krb5.conf passt dann so eigentlich schon. Die domain_realms werden per SRV Records im Router gesetzt und ansonsten ohnehin vom Client realm her aufgelöst. Für den LDAP-Client der das statt PAM nutzt aber unbedingt passend setzen oder im Router klären (am besten beides).
[libdefaults]
default_realm = DOMAIN.TLD
# DNS-SRV-Records nicht gesetzt? Dann hier false
# dns_lookup_realm = false
# dns_lookup_kdc = false
# Werden im Server in LDAP gesetzt, der Client muss aber wissen wie lang seine Tickets gültig sind
ticket_lifetime = 24h
# wie lange Tickets erneuert werden sollen die neu angefragt werden (muss sonst mit kinit -r 7d festgelegt werden)
renew_lifetime = 7d
# Ticket kann an andere Dienste weitergeleitet werden
forwardable = true
# Ticket kann durch andere Hosts weiterverwendet werden
proxiable = true
# Eigenen hostnamen über DNS reverse-auflösen. Das ist historisch, brauchen wir nicht
rdns = false
[realms]
DOMAIN.TLD = {
kdc = auth.domain.tld
admin_server = auto.domain.tld
default_domain = domain.tld <-- Das kann man setzen, braucht man aber nur für Translation Kerberbos 4 zu 5
# This is always set and the default, maps user@DOMAIN.TLD to the local user user
auth_to_local = DEFAULT
#
# TODO auth_to_local =
}
[domain_realm]
domain.tld = DOMAIN.TLD
#eigentlich unnötig?
#.domain.tld = DOMAIN.TLD
Der Client kann jetzt an sich Tickets holen. -->
root@server:~# apt install krb5-user krb5-kdc krb5-admin-server krb5-kdc-ldap
hostname auth.domain.tld nutzen
Als erstes brauchen wir LDAP als Backend für 🦴 Kerberos Principals und ein paar Kerberos-Server-Einstellungen wie Ticket-Lebenszeit, etc.
schema kopieren, 60kerberos.ldif liegt in /usr/local/share/dirsrv/data --> /etc/dirsrv/slapd-ldap/schema
Indizes
dsconf INSTANCE backend index add --attr krbPrincipalName --index-type eq "dc=domain,dc=tld" dsconf INSTANCE backend index add --attr krbCanonicalName --index-type eq "dc=domain,dc=tld"
Instanz neustarten
Wir brauchen in LDAP jetzt 2 User für die Dienste kdc und kadmin (Verteiler von Tickets und Kerberos-Admin)
Wo die liegen ist zwar egal, aber ou=Dienste,dc=domain,dc=tld bietet sich natürlich an:
uid=kdc,ou=Dienste und uid=kadmin,ou=Dienste anlegen (objectClass account und simpleSecurityObject)
dn: uid=kadmin,ou=services,dc=domain,dc=tld
objectClass: account
objectClass: simpleSecurityObject
objectClass: top
uid: kadmin
description: Kerberos Admin-Server-Account
dn: uid=kdc,ou=services,dc=domain,dc=tld
objectClass: account
objectClass: simpleSecurityObject
objectClass: top
uid: kdc
description: Kerberos Key-Distribution-Center (TGT-Server)
kann man in Cockpit machen, bissel hakelig aber geht.
# 1. Provide the KDC permission to read everything and write lockout data
dn: dc=domain,dc=tld
changetype: modify
add: aci
aci: (targetattr="*")(version 3.0; acl "KDC Read Access"; allow (read,search,compare) userdn="ldap:///uid=kdc,ou=services,dc=domain,dc=tld";)
-
add: aci
aci: (targetattr="krbLastSuccessfulAuth || krbLastFailedAuth || krbLoginFailedCount || krbLastAdminUnlock")(version 3.0; acl "KDC Write Access for Lockouts"; allow (write) userdn="ldap:///uid=kdc,ou=services,dc=domain,dc=tld";)
# 2. Provide Kadmin full access to the whole suffix (to manage keys/principals)
dn: dc=domain,dc=tld
changetype: modify
add: aci
aci: (targetattr="*")(version 3.0; acl "Kadmin Full Access"; allow (all) userdn="ldap:///uid=kadmin,ou=services,dc=domain,dc=tld";)
# 3. Deny everyone else from seeing the actual keys (The Security Layer)
dn: dc=domain,dc=tld
changetype: modify
add: aci
aci: (targetattr="krbPrincipalKey || krbExtraData")(version 3.0; acl "Restrict Kerberos Keys"; deny (read,search,compare) userdn!="ldap:///uid=kdc,ou=services,dc=domain,dc=tld" AND userdn!="ldap:///uid=kadmin,ou=services,dc=domain,dc=tld" AND userdn!="ldap:///cn=Directory Manager";)
Kann man in Cockpit manuell adden.
Für Kerberos ist wichtig, dass wir allen DNs die einen krbPrincipalKey haben (also ein Passwort) ähnliche Rechte wie für userPassword geben und beiden uid=kdc und uid=kadmin ähnliche Rechte wie anderen lookup- oder users-Diensten die Nutzerdaten selektiv lesen /schreiben können.
https://wiki.debian.org/LDAP/OpenLDAPSetup#Kerberos
/etc/krb5kdc/kdc.conf
[kdcdefaults]
kdc_ports = 750,88
[realms]
DOMAIN.TLD = {
database_module = openldap_ldapconf
database_name = /var/lib/krb5kdc/principal
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /etc/krb5kdc/stash
kdc_ports = 750,88
# Wird in LDAP überschrieben
# max_life = 24h 0m 0s
# means you can get a new ticket with the old one
# max_renewable_life = 7d 0h 0m 0s
#master_key_type = aes256-cts
#supported_enctypes = aes256-cts:normal aes128-cts:normal
# default_principal_flags = +preauth
}
[dbmodules]
openldap_ldapconf = {
db_library = kldap
#ldap_kerberos_container_dn = cn=krbContainer,ou=kerberos,ou=Services,dc=domain,dc=tld
ldap_kerberos_container_dn = cn=krbContainer,ou=Dienste,dc=domain,dc=tld
# if either of these is false, then the ldap_kdc_dn needs to
# have write access as explained above
disable_last_success = true
disable_lockout = true
ldap_conns_per_server = 5
ldap_servers = ldaps://ldap.domain.tld/
# this object needs to have read rights on
# the realm container, principal container and realm sub-trees
#ldap_kdc_dn = "uid=kdc,ou=kerberos,ou=Services,dc=domain,dc=tld"
ldap_kdc_dn = "uid=kdc,ou=Dienste,dc=domain,dc=tld
# this object needs to have read and write rights on
# the realm container, principal container and realm sub-trees
#ldap_kadmind_dn = "uid=kadmin,ou=kerberos,ou=Services,dc=domain,dc=tld"
ldap_kadmind_dn = "uid=kadmin,ou=service,dc=domain,dc=tld"
# this file will be used to store plaintext passwords used
# to connect to the LDAP server
ldap_service_password_file = /etc/krb5kdc/service.keyfile
# OR, comment out ldap_kdc_dn, ldap_kadmind_dn and
# ldap_service_password_file above and enable the following
# two lines, if you skipped the step of creating entries/users
# for the Kerberos servers
#ldap_kdc_sasl_mech = EXTERNAL
#ldap_kadmind_sasl_mech = EXTERNAL
#ldap_servers = ldapi:///
}
Jetzt nutzen wir das coole kdb5_ldap_util um den Kerberos-Container in LDAP anzulegen und für die beiden Dienst-DNs die Passwörter in /etc/krb5/service.keyfile zu speichern.
ldap-sachen anlegen:
#neues DB-Master passwort setzen, cn=krbContainer,ou=Dienste,dc=domain,dc=tld anlegen
root@auth:~# kdb5_ldap_util -D "cn=Directory Manager" create -subtrees dc=domain,dc=tld -r DOMAIN.TLD -s -H ldaps://ldap.domain.tld/
Jetzt die Passwörter für uid=kdc und uid=kadmin hinterlegen die Kerberbos braucht um sich zu LDAP zu verBinden
root@auth:~# kdb5_ldap_util -D "cn=Directory Manager" stashsrvpw -f /etc/krb5kdc/service.keyfile uid=kdc,ou=services,dc=domain,dc=tld
root@auth:~# kdb5_ldap_util -D "cn=Directory Manager" stashsrvpw -f /etc/krb5kdc/service.keyfile uid=kadmin,ou=services,dc=domain,dc=tld
389ds ersetzt durch PAM Pass-Through plugin
root@auth:~# apt install libpam-krb5
read up on https://docs.redhat.com/en/documentation/red_hat_directory_server/11/html/administration_guide/sasl
Mapping in Cockpit -> SASL Settings adden:
name DOMAIN.TLD-Realm mapping
regexp \(.*\)@DOMAIN.TLD
base dc=domain,dc=tld
filter (uid=\1)
prio 10
Plugin kann man in Cockpit anschalten.
Config kann man in Cockpit hinzufügen:
"Domain.tld PTA"
include suffix dc=domain,dc=tld
exclude suffix ou=services,dc=domain,dc=tld
--id-attr uid
das exclude brauchen wir weil da kdc und kadmin mit normalem userpass liegen
Service ist ldapserver und das zeigt dann auf /etc/pam.d/ldapserver
pam findet jetzt den user nicht (getpwnam) obwohl kerberos sagt dass er ok ist. /etc/pam.d/ldapserver erstellen
# /etc/pam.d/ldapserver
# 1. Verify the password via Kerberos
auth required pam_krb5.so minimum_uid=100
# 2. Always succeed the "account" check
# This bypasses the need for the user to be in /etc/passwd
account required pam_permit.so
Voila.
Auch wenn Du müde bist, in cn=DOMAIN.TLD,cn=krbContainer,ou=Dienste,dc=domain,dc=tld
unbedingt noch krbMaxRenewableAge auf 604800 setzen und krbMaxTicketLife auf 86400, diese Werte auch in sssd.conf für Clienten eintragen.
kadmin.local starten, neues principal für den ldap-server erstellen:
kadmin.local
addprinc -randkey ldap/ldapserverhostname.domain.tld
ktadd -k /etc/dirsrv/slapd-INSTANCE/ds.keytab ldap/ldapserverhostname.domain.tld
q
chmod 600 /etc/dirsrv/slapd-INSTANCE/ds.keytab
Diesen key noch in /etc/default/dirsrv-INSTANCE exportieren:
KRB5_KTNAME=/etc/dirsrv/slapd-INSTANCE/ds.keytab
export KRB5_KTNAME
Jetzt an die Instances denken. Die sind standardmässig unter /etc/krb5kdc/kadm5.acl
/etc/krb5kdc/kadm5.acl
*/admin@DOMAIN.TLD *
kadmin/admin ist der einzige Principal der in der Kerboros-Datebank alles darf. Wenn Du das wieder vergisst: Das legt NICHT fest welcher Principal welchen Service nutzen darf.
An dieser Stelle macht es Sinn Kerberos erst nach OpenLDAP starten zu lassen, bzw. auf dessen Verfügbarkeit zu warten:
root@server:~# systemctl edit krb5-kdc
erstellt eine Datei /etc/systemd/system/krb5-kdc.service.d/override.conf, wir tragen ein:
[Unit]
After=dirsrv@INSTANCE.service network-online.target
Requires=dirsrv@INSTANCE.service network-online.target
[Service]
Restart=on-failure
RestartSec=5
Das gleiche für
root@server:~# systemctl edit krb5-admin-server
erstellt eine Datei /etc/systemd/system/krb5-admin-server.service.d/override.conf, wir tragen ein:
[Unit]
After=dirsrv@INSTANCE.service network-online.target
Requires=dirsrv@INSTANCE.service network-online.target
[Service]
Restart=on-failure
RestartSec=5
Kleiner Hinweis, hier sowohl After= als auch Requires= zu nutzen sorgt dafür, dass zwingend auf den erfolgreichen Start von 389DS gewartet wird und nicht nur die Startreihenfolge (After=) bzw. die Abhängigkeit (Requires=).
Ausserdem nutzen wir als Safeguard in beiden Fällen bei Fehlern den Dienst neuzustarten. Es sollte Requires= genügen aber ich würde argumentieren hier ist es ok ein bisschen zu tricksen, denn ob der Dienst neustarten musste sieht man im Log, aber immerhin geht er dann noch.
cn=config SASL stuff for GSSAPI:
If you configure the olcSaslRealm then it will be inserted as an extra component in the authorization DN, regardless of any Kerberos realms in use. For domain, if you set olcSaslRealm to domain.tkd then you will get:
das brauchen wir also nicht zwingend, auch olcSaslHost scheint entweder ignoriert zu werden oder ist mit localhost gut dran.