Why Kerberos
It solves a basic problem of a multi-service network. The user should not retype a password every time, the password should not travel over the network, and the server needs proof that the user is who they claim to be. Kerberos has provided this SSO since 1988 (MIT, project Athena). Today:
- Active Directory, Kerberos from the very start of AD; krb5 + LDAP through MS extensions
- FreeIPA, the RHEL SSO stack: Kerberos + 389-DS + DNS + CA
- NFSv4 sec=krb5p, the only production-grade auth for NFS in a multi-user environment
- SSH GSSAPI, proxying of Kerberos authentication
- Hadoop/Kafka secure mode, krb for service-to-service auth
Not meant for general public internet services (OIDC/OAuth2 rules there), but in the corporate datacenter it is the standard.
Main concepts
| Term | What |
|---|---|
| Realm | administrative domain (typically an UPPERCASE domain name: EXAMPLE.COM) |
| Principal | identity within a realm: alice@EXAMPLE.COM (user) or host/server.example.com@EXAMPLE.COM (service) |
| KDC | Key Distribution Center, the server that issues tickets. Made of AS + TGS |
| AS (Authentication Service) | initial authentication, issues the TGT |
| TGS (Ticket Granting Service) | issues service tickets in exchange for the TGT |
| TGT | Ticket Granting Ticket, the main ticket, for ~10 hours |
| Service Ticket | a "pass" to one specific service, for minutes to hours |
| Keytab | file with long-term keys for service principals (no password) |
All keys are symmetric. That is both a strength (fast cryptography) and a limit (the KDC must know the keys of every principal).
Authentication flow
Simplified:
Client KDC (AS+TGS) Service
│ │ │
│ AS-REQ (I'm alice@EXAMPLE.COM) │ │
├─────────────────────────────────────►│ │
│ AS-REP (TGT, encrypted with │ │
│ alice's password-derived key) │ │
│◄─────────────────────────────────────┤ │
│ [client decrypts → if pw correct] │ │
│ │ │
│ TGS-REQ (TGT + "I want service │ │
│ nfs/file.example.com") │ │
├─────────────────────────────────────►│ │
│ TGS-REP (service ticket, │ │
│ encrypted with service's key) │ │
│◄─────────────────────────────────────┤ │
│ │ │
│ AP-REQ (service ticket) │ │
├─────────────────────────────────────────────────────────►│
│ AP-REP (mutual auth, optional) │
│◄─────────────────────────────────────────────────────────┤
The key point: the password travels only in the first message (and even then, not the password itself but a key derived from it). After that, only tickets move.
Principals
Format:
primary[/instance]@REALM
alice@EXAMPLE.COM ← user
host/server.example.com@EXAMPLE.COM ← service "host" (for SSH/SCP)
nfs/server.example.com@EXAMPLE.COM ← NFS service
HTTP/web.example.com@EXAMPLE.COM ← Apache/Nginx with SPNEGO
alice/admin@EXAMPLE.COM ← privileged alice
Service principals are named with the scheme service/fqdn@REALM, where
service is a known type (host, HTTP, nfs, ldap, ftp).
Installing MIT Kerberos on Linux
# Server (KDC)
apt install krb5-kdc krb5-admin-server # Debian/Ubuntu
dnf install krb5-server krb5-server-ldap # RHEL
# Clients
apt install krb5-user # Debian/Ubuntu
dnf install krb5-workstation # RHEL
/etc/krb5.conf:
[libdefaults]
default_realm = EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = true # KDC via SRV records
ticket_lifetime = 24h
renew_lifetime = 7d
[realms]
EXAMPLE.COM = {kdc = kdc1.example.com
kdc = kdc2.example.com
admin_server = kdc1.example.com
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
The KDC needs closely synchronized time (max skew ~5 minutes).
Without NTP/chrony, Kerberos fails with Clock skew too great.
Daily commands
kinit alice # request a TGT (will ask for password)
klist # show tickets in the cache
klist -e # with encryption type
kdestroy # clear the cache
kpasswd # change password
# From keytab without interaction
kinit -k -t /etc/krb5.keytab host/server.example.com
# Renew TGT (if within lifetime)
kinit -R
The cache lives by default in /tmp/krb5cc_$UID (FILE-based) or in
Keyring/KCM on modern distros.
Keytab, for services
Services (sshd, nfs, http) cannot prompt for a password. Their long-term keys live in a keytab (binary file):
# Create a service principal on the KDC
kadmin.local -q "addprinc -randkey nfs/server.example.com"
# Export to a keytab
kadmin.local -q "ktadd -k /etc/krb5.keytab nfs/server.example.com"
# Use it on the client
ktutil
> read_kt /etc/krb5.keytab
> list -e
# Keytab version
klist -kt /etc/krb5.keytab
A keytab is a secret. Mode 600, owner = root (or the service user). A leak means takeover of the service.
Kernel support: KEYRING / KCM
Tickets used to live in the file /tmp/krb5cc_$UID. Modern distros
use:
- KEYRING, storage in the kernel keyring, per-namespace isolation
- KCM (Kerberos Cache Manager), a daemon, handy for multi-user
- a collection of caches (several realms at once)
Configured through default_ccache_name = KEYRING:persistent:%{uid}.
SSH over GSSAPI/Kerberos
Once you have run kinit, ssh server.example.com will not ask for a
password:
Server:
# /etc/ssh/sshd_config
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
Client:
# ~/.ssh/config
Host *.example.com
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes # forward the TGT to the server
With GSSAPIDelegateCredentials yes, the server gets a copy of your TGT
and can act on your behalf onward (to NFS or other SSH hosts).
MIT Kerberos vs Heimdal vs AD
| Trait | MIT | Heimdal | Microsoft AD |
|---|---|---|---|
| Lineage | reference | independent implementation | proprietary |
| Default on Linux | RHEL/Fedora/Debian | FreeBSD, Samba (old) | through samba/realmd |
| Active dev | yes | slowed down | yes |
| Compatibility with AD | good | good | native |
Most Linux documentation assumes MIT.
When something goes wrong
Clock skew too great, the KDC and the client drifted apart in time by more thanclockskew(default 5min). Turn on [[chrony-and-ntp|chrony]].KDC has no support for encryption type, the client requests DES/RC4, the KDC disabled them (security update). Updatedefault_tgs_enctypes/default_tkt_enctypesto modern ones (aes256-cts).Cannot find KDC for requested realm, DNS SRV is not configured, ordns_lookup_kdc = falsewithout an explicitkdc =in[realms].Permission denied (gssapi-with-mic)in SSH, no TGT (klistis empty), or the service principalhost/server@REALMis missing on the KDC, or the server keytab is stale.KRB_AP_ERR_TKT_EXPIRED, the service ticket expired by timeout.kinit -Rrenews the TGT, and new service tickets follow.- Keytab stopped working after the principal password changed
the KVNO (key version number) went up. Reissue the keytab:
ktadd -k /etc/krb5.keytab service/host. Server not found in Kerberos database, the service principal was not created on the KDC, or the request goes to the wrong hostname (mismatched DNS vs principalhost/).