Verify SSO SSH with Kerberos and Microsoft AD

Steps to verify Single Sign-On (SSO) SSH integration with Kerberos and Microsoft Active Directory on Linux systems.

On pxd-rproxy1 as root, test the integration using user tony with password Supersecret!.

Check SSH SSO

Check if SSH SSO works. If it does, you can skip the subsequent tests, as this verifies the overall integration.

To test SSH SSO, log in as the domain user tony from another system configured for Kerberos, using SSO without entering a password. This flow involves:

  • Kerberos: Obtains a ticket-granting ticket (TGT) and service ticket for SSH.
  • SSSD: Handles authentication and identity mapping from AD.
  • PAM: Integrates SSSD for authentication in SSH.
  • AD: Provides user credentials and group memberships.

If login succeeds, it confirms that PAM, SSSD, Kerberos, and AD are all functioning correctly together.

Get a forwardable ticket:

su - tony
echo "Supersecret!" | kinit -f

Get Ticket as Domain User

Check if domain user tony can obtain an AD Kerberos ticket using kinit.

echo "Supersecret!" | kinit tony@C2.ORG

and

echo "Supersecret!" | kinit tony

Check the result/ticket:

klist
Show me
root@pxd-rproxy1:/etc/apache2# kinit tony@C2.ORG
Password for tony@C2.ORG:
root@pxd-rproxy1:/etc/apache2# kinit tony
Password for tony@C2.ORG:
root@pxd-rproxy1:/etc/apache2#
vagrant@pxd-rproxy1:~$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: tony@C2.ORG

Valid starting     Expires            Service principal
07/30/25 05:16:04  07/30/25 15:16:04  krbtgt/C2.ORG@C2.ORG
        renew until 07/31/25 05:16:00

Check SRV Records

SRV records are DNS service records that point to hosts providing specific services, like Kerberos or LDAP. This verification ensures that the domain c2.org correctly advertises its Kerberos and LDAP services, which are essential for clients to locate and connect to the AD domain controllers.

host -t SRV _kerberos._udp.c2.org
host -t SRV _ldap._tcp.c2.org
Show me
root@pxd-rproxy1:/etc/apache2# host -t SRV _kerberos._udp.c2.org
_kerberos._udp.c2.org has SRV record 0 100 88 pxd-ad.c2.org.
root@pxd-rproxy1:/etc/apache2#
root@pxd-rproxy1:/etc/apache2# host -t SRV _ldap._tcp.c2.org
_ldap._tcp.c2.org has SRV record 0 100 389 pxd-ad.c2.org.
root@pxd-rproxy1:/etc/apache2#

Test AD Authentication

Try to log in as AD user tony:

su - tony@C2.ORG
su - tony

Or check using getent:

getent passwd tony@C2.ORG
getent passwd tony
Show me
root@pxd-rproxy1:/etc/apache2# su - tony@C2.ORG
Creating directory '/home/tony@c2.org'.
tony@pxd-rproxy1:~$ exit
logout
root@pxd-rproxy1:/etc/apache2# su - tony
tony@pxd-rproxy1:~$ exit
logout
root@pxd-rproxy1:/etc/apache2# getent passwd tony@C2.ORG
tony:*:708801104:708800513:tony:/home/tony@c2.org:/bin/bash
root@pxd-rproxy1:/etc/apache2# getent passwd tony
tony:*:708801104:708800513:tony:/home/tony@c2.org:/bin/bash
root@pxd-rproxy1:/etc/apache2#

Check A-Record and PTR-Record

Kerberos relies on accurate DNS resolution for security and functionality. Service Principal Names (SPNs) include hostnames, so forward (A-record: hostname to IP) and reverse (PTR-record: IP to hostname) resolution must match to prevent spoofing and ensure trust. Mismatches can cause Kerberos authentication failures. For this to work correctly with Vagrant nodes it is important to not use hostname1.

Assuming the AD Domain Controller is pxd-ad with IP 192.168.61.11, and we’re checking for pxd-rproxy1 (replace with its actual IP if known):

Check A-record (forward resolution):

dig A pxd-rproxy1
dig A pxd-rproxy1.c2.org
Show me
root@pxd-rproxy1:/etc/apache2# dig A pxd-rproxy1

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> A pxd-rproxy1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15504
;; flags: qr aa rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;pxd-rproxy1.                   IN      A

;; ANSWER SECTION:
pxd-rproxy1.            0       IN      A       127.0.5.1

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Wed Jul 30 08:02:20 UTC 2025
;; MSG SIZE  rcvd: 56
root@pxd-rproxy1:/etc/apache2# dig A pxd-rproxy1.c2.org

; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> A pxd-rproxy1.c2.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50067
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;pxd-rproxy1.c2.org.            IN      A

;; ANSWER SECTION:
pxd-rproxy1.c2.org.     3600    IN      A       192.168.60.10

;; Query time: 0 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Wed Jul 30 08:02:44 UTC 2025
;; MSG SIZE  rcvd: 63

root@pxd-rproxy1:/etc/apache2#

The second dig A pxd-rproxy1.c2.org resolves the fully qualified domain name (FQDN) to 192.168.60.10, which appears to be the correct external IP address from the DNS server. This is good for Kerberos, as it ensures proper forward resolution for the FQDN, which is critical for obtaining tickets and avoiding spoofing risks. However, ensure that reverse (PTR) resolution matches this IP back to the correct FQDN to maintain trust.

Check PTR-record (reverse resolution, e.g., if pxd-rproxy1 IP is 192.168.60.10):

host 192.168.60.10

or

dig PTR 10.60.168.192.in-addr.arpa

Verify the results match the expected hostname and IP.

config.vm - Vagrantfile | Vagrant | HashiCorp Developer https://developer.hashicorp.com/vagrant/docs/vagrantfile/machine_settings


  1. Typicall with a Vagrant environment the dig A pxd-rproxy1 command resolves the short hostname to an IP address similar to 127.0.1.1, which is a local loopback address. This is because in /etc/hosts or the local resolver (e.g., systemd-resolved). For Kerberos, this can be problematic if it interferes with FQDN resolution, as Kerberos relies on consistent hostname-to-IP mapping for security. It’s not ideal, as it may cause mismatches in Service Principal Names (SPNs) or lead to authentication failures if the system uses the short name internally.

    root@pxd-rproxy1:/etc/apache2# dig A pxd-rproxy1
    
    ; <<>> DiG 9.18.30-0ubuntu0.22.04.2-Ubuntu <<>> A pxd-rproxy1
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15504
    ;; flags: qr aa rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 65494
    ;; QUESTION SECTION:
    ;pxd-rproxy1.                   IN      A
    
    ;; ANSWER SECTION:
    pxd-rproxy1.            0       IN      A       127.0.5.1
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
    ;; WHEN: Wed Jul 30 08:02:20 UTC 2025
    ;; MSG SIZE  rcvd: 56
    

    For example

    127.0.1.1       packer-ubuntu-bionic-amd64-lxc
    127.0.0.1       localhost
    ::1             localhost ip6-localhost ip6-loopback
    ff02::1         ip6-allnodes
    ff02::2         ip6-allrouters
    
    127.0.2.1 c2d-ubuntu-bionic-lxc c2d-ubuntu-bionic-lxc
    127.0.3.1 pxd-ubuntu22-lxc pxd-ubuntu22-lxc
    127.0.4.1 pxd-ubuntu22-desktop-lxc pxd-ubuntu22-desktop-lxc
    127.0.5.1 pxd-rproxy1 pxd-rproxy1
    
    127.0.0.1       localhost
    ::1             localhost ip6-localhost ip6-loopback
    ff02::1         ip6-allnodes
    ff02::2         ip6-allrouters
    
    127.0.1.1 pxd-rproxy1 pxd-rproxy1
    
     ↩︎


Last modified August 27, 2025: phx ad draft C2-633 (4731749)