Kerberos (GSSAPI) authentication
skaidb can authenticate clients with a Kerberos ticket instead of a
password, following an external-authentication model: a KDC vouches for
the principal, and skaidb only maps that principal to a role. Both the binary
protocol (drivers, skaidbsh) and REST/HTTP (browser SSO via SPNEGO) support
it, alongside — not instead of — SCRAM password auth.
Availability
Kerberos links the system Kerberos C library, so it is compiled into the
glibc .deb/.rpm builds (and macOS/Windows client builds when enabled),
but not the static-musl .tar.gz — that binary ships without it. Starting
a musl build with auth.gssapi_enabled = true is a hard startup error, never a
silent no-op. The .deb declares a runtime dependency on libgssapi-krb5-2
(.rpm: krb5-libs), so a minimal host pulls the library automatically.
Prerequisites
- A Kerberos realm and KDC (MIT krb5, Active Directory, FreeIPA, …).
- A service principal for skaidb, conventionally
skaidb/<host>@REALM, and its keytab exported to each skaidb node. - Clients with a ticket-granting ticket (from
kinit, a login manager, or a client keytab). - Clocks synchronized within the KDC's skew tolerance (default 5 minutes) — Kerberos rejects tickets outside it. Run NTP everywhere.
Create the service principal and keytab (MIT krb5)
On the KDC:
kadmin.local -q "addprinc -randkey skaidb/[email protected]"
kadmin.local -q "ktadd -k /etc/skaidb/skaidb.keytab skaidb/[email protected]"
Copy skaidb.keytab to the node, own it by the skaidb user, and lock it down:
chown skaidb:skaidb /etc/skaidb/skaidb.keytab
chmod 640 /etc/skaidb/skaidb.keytab
Server configuration
In /etc/skaidb/skaidb.toml:
[auth]
scram_enabled = true # SCRAM stays available alongside GSSAPI
gssapi_enabled = true
gssapi_keytab = "/etc/skaidb/skaidb.keytab"
# Optional: accept only this exact SPN. Empty accepts whatever the keytab holds
# (the usual case — the acceptor tries every key in the keytab).
gssapi_service_principal = "skaidb/[email protected]"
[encryption]
# Strongly recommended: GSSAPI authenticates the client but does NOT encrypt
# the SQL stream. Run it inside client TLS for confidentiality.
client_tls = "required"
Environment-variable equivalents (e.g. for Docker): SKAIDB_GSSAPI_ENABLED,
SKAIDB_GSSAPI_KEYTAB, SKAIDB_GSSAPI_SERVICE_PRINCIPAL.
The server reads the keytab once at startup (as the GSSAPI KRB5_KTNAME) and
resolves the realm from the ambient /etc/krb5.conf. A missing or unreadable
keytab fails startup loudly.
Create the external users
A Kerberos user is external — passwordless, keyed by its principal. The
principal contains @ and ., so double-quote it:
CREATE USER "[email protected]" GSSAPI;
GRANT SELECT ON DATABASE app TO "[email protected]";
The principal maps exactly to its own-named role; grants and role inheritance work identically to password users. An external user cannot authenticate with a password, and a password user is never reachable through the Kerberos path.
Client usage
skaidbsh
kinit [email protected]
skaidbsh -H node1.example.com:7000 --tls --tls-ca /etc/skaidb/tls/ca.crt \
--auth-mechanism gssapi \
--gssapi-spn skaidb/[email protected] \
-u [email protected] \
-e "SELECT 1"
No password is sent — the ambient ticket cache is used. --user is the client
principal; the authenticated identity comes from the ticket. Environment
equivalents: SKAIDB_AUTH_MECHANISM=gssapi, SKAIDB_GSSAPI_SPN=….
Rust driver
// kinit first; uses the ambient ticket cache.
let client = Client::connect_gssapi_tls(
&["node1.example.com:7000".into()],
"[email protected]", // client principal
"skaidb/[email protected]", // target service principal
Some(tls), // recommended
)?;
REST / browser (SPNEGO)
When gssapi_enabled, the REST endpoints accept Authorization: Negotiate
<token> (RFC 4559) and advertise it in the 401 WWW-Authenticate challenge
(alongside Basic). A Kerberos-configured browser gets single-sign-on to the
UI/REST; curl --negotiate -u : https://node1.example.com:7443/query -d …
works too.
SPN note for HTTP clients: browsers and
curl --negotiaterequest the service principalHTTP/<host>@REALM(the HTTP convention). To serve them, add anHTTP/<host>@REALMprincipal to the keytab as well — the acceptor tries every key in the keytab. Native skaidb clients targetskaidb/<host>directly, so they need only that one.
SPNEGO over REST is single-leg (Kerberos establishes in one token); the stateless REST path does not carry a multi-round negotiation.
How it works
- Mechanism negotiation. The client handshake carries a mechanism selector
(
AuthStart); SCRAM is the default and stays wire-compatible, so old clients are unaffected. GSSAPI adds a repeatableAuthTokenframe for the context exchange. - Context establishment. The client and server run a GSS context negotiation (mutual authentication + confidentiality required); the server reads the cryptographically-authenticated principal.
- Identity mapping. The principal is looked up as an external user; its
own-named role is the acting role. The username a client puts in
AuthStartis untrusted — only the GSS principal counts.
Operational caveats
- Clock skew. The most common failure. Symptom:
Clock skew too great. Fix: NTP on the KDC, nodes, and clients. - DNS / SPN mismatch. The client's target SPN must match a key in the
server's keytab and a principal the KDC knows. Reverse-DNS canonicalization
can rewrite the host in the SPN unexpectedly; disable it (
rdns = falseinkrb5.conf) or pingssapi_service_principaland pass the client a matching--gssapi-spn. - Keytab permissions. The keytab is a long-term secret. Keep it
0640 skaidb:skaidb, never world-readable, never in version control. - TLS. GSSAPI authenticates; it does not encrypt the SQL stream. Run
client_tls = requiredfor confidentiality (see the encryption docs). - Platform. glibc only today. The static-musl build has no Kerberos; macOS/Windows client builds are added as their CI builds are validated.
Troubleshooting
| Symptom | Likely cause |
|---|---|
gssapi init failed (did you kinit?) on the client |
No ticket — run kinit, check klist. |
no GSSAPI user for principal "…" |
The principal has no CREATE USER "…" GSSAPI account. |
GSSAPI authentication is not enabled |
auth.gssapi_enabled is false, or a non-kerberos (musl) build. |
Startup: gssapi_enabled = true but this build lacks the kerberos feature |
Running the musl binary — use the glibc .deb/.rpm. |
Clock skew too great |
Time drift — sync NTP. |
Server not found in Kerberos database |
The target SPN isn't a KDC principal / not in the keytab. |
Security scope (current)
The GSS context requires mutual authentication and confidentiality, and runs
inside client TLS. Channel binding (binding the GSS context to the outer TLS
channel, tls-server-end-point) is not implemented;
always run GSSAPI inside TLS, which already authenticates the
server and prevents token relay to a different endpoint.