Kerberos Authentication Flow in SSH: Technical Deep Dive into Laptop-to-Server Authentication Protocol Exchanges


2 views

When using Kerberos with SSH across multiple hops (Laptop → Server1 → Server2), the authentication flow leverages ticket-granting tickets (TGTs) and service tickets for seamless authentication. Here's the technical breakdown:


1. [Laptop] User enters: ssh username@server1.example.com
2. [Laptop] SSH client sends username to Server1
3. [Server1] Checks for existing Kerberos credentials:
   - If none found, falls back to password auth
4. [Laptop] User enters password (PAM authentication occurs)
5. [Server1] Validates credentials with KDC:
   - AS-REQ: Authentication Service Request to KDC
   - AS-REP: Receives TGT encrypted with user's secret key

1. [Server1] User executes: ssh server2.example.com
2. [Server1] Presents TGT to KDC for service ticket:
   - TGS-REQ: Ticket Granting Service Request for host/server2.example.com
3. [KDC] Returns:
   - TGS-REP: Service ticket encrypted with Server2's secret key
4. [Server1] Presents service ticket to Server2
5. [Server2] Validates ticket and establishes session

For this flow to work, ensure proper configuration:


# /etc/krb5.conf on all hosts
[libdefaults]
    default_realm = EXAMPLE.COM
    forwardable = true
    proxiable = true

# /etc/ssh/sshd_config on servers
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes

Use these commands to troubleshoot authentication issues:


# Check ticket cache
klist

# Obtain new ticket
kinit username

# Verify SSH with Kerberos debugging
ssh -vvv -K server2.example.com

# Check KDC logs
tail -f /var/log/krb5kdc.log

Here's how to implement Kerberos-authenticated SSH in Python:


import gssapi
from paramiko import SSHClient, AutoAddPolicy

# Create security context
name = gssapi.Name("host@server2.example.com")
ctx = gssapi.SecurityContext(name=name, usage="initiate")

# Establish SSH connection
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect('server2.example.com', gss_auth=True)

# Execute remote command
stdin, stdout, stderr = client.exec_command('hostname')
print(stdout.read().decode())

client.close()

When using Kerberos with SSH in a multi-hop scenario, the authentication process involves several protocol exchanges between four entities: Client (Laptop), Intermediate Server (Server1), Target Server (Server2), and the Kerberos KDC (Key Distribution Center). Here's the complete breakdown:

1. Laptop → Server1: SSH_MSG_USERAUTH_REQUEST (username)
2. Server1 → Laptop: SSH_MSG_USERAUTH_FAILURE (supported auth methods)
3. Laptop → Kerberos KDC: AS_REQ (Authentication Service Request)
4. Kerberos KDC → Laptop: AS_REP (TGT - Ticket Granting Ticket)
5. Laptop → Kerberos KDC: TGS_REQ (Service Ticket for Server1)
6. Kerberos KDC → Laptop: TGS_REP (Service Ticket + session key)
7. Laptop → Server1: SSH_MSG_USERAUTH_REQUEST (GSSAPI token)
8. Server1 → Kerberos KDC: AP_REQ (Ticket verification)
9. Kerberos KDC → Server1: AP_REP (Verification result)
10. Server1 → Laptop: SSH_MSG_USERAUTH_SUCCESS

The key difference in the second hop is credential forwarding:

1. Server1 → Server2: SSH_MSG_USERAUTH_REQUEST (username)
2. Server2 → Server1: SSH_MSG_USERAUTH_FAILURE (supports GSSAPI-with-mic)
3. Server1 → Server2: SSH_MSG_USERAUTH_REQUEST (GSSAPI token)
   - Uses delegated credentials from initial authentication
4. Server2 → Kerberos KDC: AP_REQ (Ticket verification)
5. Kerberos KDC → Server2: AP_REP (Verification result)
6. Server2 → Server1: SSH_MSG_USERAUTH_SUCCESS

For this to work, ensure these settings in /etc/ssh/sshd_config on both servers:

GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
GSSAPIStrictAcceptorCheck no  # Important for credential delegation

And in /etc/krb5.conf on all systems:

[libdefaults]
    default_realm = YOURDOMAIN.COM
    forwardable = true
    proxiable = true

[realms]
    YOURDOMAIN.COM = {
        kdc = kerberos.yourdomain.com
        admin_server = kerberos.yourdomain.com
    }

Use these commands to verify the Kerberos ticket flow:

# Check ticket cache
klist

# Enable SSH debugging
ssh -vvv user@server1

# Verify GSSAPI support
ssh -Q gssapi-keyex
ssh -Q gssapi-with-mic
import paramiko
from gssapi import Credentials

# Acquire delegated credentials
krb_creds = Credentials(usage='initiate')

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

# Connect with GSSAPI
ssh.connect('server2', gss_auth=True, gss_kex=True, 
           gss_deleg_creds=True, gss_host='server2.yourdomain.com')

stdin, stdout, stderr = ssh.exec_command('hostname')
print(stdout.read().decode())