Linux AD Auth - Implementation

Phase 0: 802.1X Supplicant Configuration (NetworkManager)

This section addresses the "password required" issue where NetworkManager prompts for a private key password even when the key has no passphrase.

This was field-tested in the home enterprise and is critical for unattended 802.1X authentication.

The Problem

When applying NetworkManager 802.1X configuration, the supplicant may prompt:

Error: Connection activation failed: Secrets were required, but not provided

Or in GUI:

"Authentication required for Wired-802.1X"
"Private key password needed"

Root Cause: NetworkManager defaults to treating the private key as password-protected. Without explicit flags, it waits for user input.

The Solution: Password Flags

Two critical flags MUST be set:

Flag Purpose

802-1x.identity-flags=0

Store identity in connection file (not as secret requiring prompt)

802-1x.private-key-password-flags=4

Key has no password - don’t prompt (value 4 = NOT_REQUIRED)

0.1 Create 802.1X Connection (Correct Method)

# Get hostname
MYHOST=$(hostname -s)
INTERFACE="eth0"  # Replace with actual interface name
DOMAIN="la.ad.chla.org"
CA_CERT="/etc/ssl/certs/CHLA-CHAIN.pem"
CLIENT_CERT="/etc/ssl/certs/${MYHOST}-eaptls.pem"
PRIVATE_KEY="/etc/ssl/private/${MYHOST}-eaptls.key"

# Create connection WITH password flags
sudo nmcli connection add \
  type ethernet \
  con-name "Wired-802.1X" \
  ifname "$INTERFACE" \
  802-1x.eap tls \
  802-1x.identity "${MYHOST}.${DOMAIN}" \
  802-1x.identity-flags 0 \
  802-1x.ca-cert "$CA_CERT" \
  802-1x.client-cert "$CLIENT_CERT" \
  802-1x.private-key "$PRIVATE_KEY" \
  802-1x.private-key-password-flags 4 \
  connection.autoconnect yes

0.2 Verify Connection File

# Check the connection file has correct flags
sudo grep -E "identity-flags|private-key-password-flags" \
  /etc/NetworkManager/system-connections/Wired-802.1X.nmconnection
Expected output:
identity-flags=0
private-key-password-flags=4

0.3 Fix Existing Connection (If Already Created)

If connection was created without flags, edit the file directly:

# Edit connection file
sudo nvim /etc/NetworkManager/system-connections/Wired-802.1X.nmconnection

# In [802-1x] section, add/modify:
# identity-flags=0
# private-key-password-flags=4

# Reload and reconnect
sudo nmcli connection reload
sudo nmcli connection up "Wired-802.1X"

0.4 Expected [802-1x] Section

[802-1x]
ca-cert=/etc/ssl/certs/CHLA-CHAIN.pem
client-cert=/etc/ssl/certs/hostname-eaptls.pem
eap=tls;
identity=hostname.la.ad.chla.org
identity-flags=0
private-key=/etc/ssl/private/hostname-eaptls.key
private-key-password-flags=4

0.5 Certificate Chain Note

If 802.1X authentication fails with "TLS handshake failed" or "unknown CA", the CA cert may need to be a chain file:

# Create combined chain (ROOT CA first, then SUB CA)
cat /usr/local/share/ca-certificates/CHLAROOTCA.crt \
    /usr/local/share/ca-certificates/CHLASUBCA.crt \
    > /tmp/chla-chain.pem

sudo cp /tmp/chla-chain.pem /etc/ssl/certs/CHLA-CHAIN.pem
sudo chmod 644 /etc/ssl/certs/CHLA-CHAIN.pem

# Update connection
sudo nmcli connection modify "Wired-802.1X" \
  802-1x.ca-cert /etc/ssl/certs/CHLA-CHAIN.pem

Phase 1: Pre-Deployment Validation

1.1 SSH to Target Workstation

# Fill in actual IP when known
ssh <admin-user>@<TARGET-IP>

1.2 System Information

echo "=== HOSTNAME ==="
hostname -f

echo "=== OS VERSION ==="
cat /etc/os-release | head -5

echo "=== NETWORK INTERFACE ==="
ip link show | grep -E "^[0-9]|ether"

1.3 AD Domain Join Status

echo "=== REALM LIST ==="
realm list

echo "=== SSSD STATUS ==="
systemctl status sssd --no-pager

echo "=== TEST USER RESOLUTION ==="
id <user>@la.ad.chla.org 2>/dev/null || echo "User not found"
getent passwd <user>@la.ad.chla.org 2>/dev/null || echo "NSS lookup failed"

Expected: realm list shows la.ad.chla.org configured, sssd running

1.4 AD Connectivity (BEFORE dACL change)

DC_IP="<DC-IP-1>"

echo "=== AD Connectivity Test ==="
{
  echo -n "DNS (53)....... "; timeout 3 bash -c "</dev/tcp/$DC_IP/53" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "Kerberos (88).. "; timeout 3 bash -c "</dev/tcp/$DC_IP/88" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "LDAP (389)..... "; timeout 3 bash -c "</dev/tcp/$DC_IP/389" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "LDAPS (636).... "; timeout 3 bash -c "</dev/tcp/$DC_IP/636" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "SMB (445)...... "; timeout 3 bash -c "</dev/tcp/$DC_IP/445" 2>/dev/null && echo "OK" || echo "FAIL"
} 2>&1

Interpreting Results:

  • If all FAIL → current dACL is blocking AD traffic (expected with Research_Onboard)

  • If all OK from admin workstation, FAIL from endpoint → confirms dACL issue

1.5 Current ISE Session

From admin workstation with netapi:

# Load credentials
dsource chla network/ise

# Check current session
netapi ise mnt session "<MAC-ADDRESS>"

Phase 2: ISE Policy Creation

2.1 Create dACL Content File

cat << 'EOF' > /tmp/dacl-linux-ad-auth-chla.txt
remark Section 1: DHCP (network bootstrap)
permit udp any any eq 67
permit udp any any eq 68

remark Section 2: DNS to Domain Controllers
permit udp any host <DC-IP-1> eq 53
permit tcp any host <DC-IP-1> eq 53
permit udp any host <DC-IP-2> eq 53
permit tcp any host <DC-IP-2> eq 53

remark Section 3: Kerberos (authentication)
permit udp any host <DC-IP-1> eq 88
permit tcp any host <DC-IP-1> eq 88
permit udp any host <DC-IP-2> eq 88
permit tcp any host <DC-IP-2> eq 88

remark Section 4: LDAP/LDAPS (directory services)
permit tcp any host <DC-IP-1> eq 389
permit tcp any host <DC-IP-1> eq 636
permit tcp any host <DC-IP-2> eq 389
permit tcp any host <DC-IP-2> eq 636

remark Section 5: Global Catalog (AD cross-domain)
permit tcp any host <DC-IP-1> eq 3268
permit tcp any host <DC-IP-1> eq 3269
permit tcp any host <DC-IP-2> eq 3268
permit tcp any host <DC-IP-2> eq 3269

remark Section 6: SMB/CIFS (AD group policy)
permit tcp any host <DC-IP-1> eq 445
permit tcp any host <DC-IP-2> eq 445

remark Section 7: kpasswd (password changes)
permit tcp any host <DC-IP-1> eq 464
permit udp any host <DC-IP-1> eq 464
permit tcp any host <DC-IP-2> eq 464
permit udp any host <DC-IP-2> eq 464

remark Section 8: NTP (time sync - critical for Kerberos)
permit udp any any eq 123

remark Section 9: ICMP (diagnostics)
permit icmp any any

remark Section 10: DENY ALL RFC1918 (zero-trust)
deny ip any 10.0.0.0 0.255.255.255
deny ip any 172.16.0.0 0.15.255.255
deny ip any 192.168.0.0 0.0.255.255

remark Section 11: Internet egress
permit ip any any
EOF

echo "Created /tmp/dacl-linux-ad-auth-chla.txt"

Replace <DC-IP-1> and <DC-IP-2> with actual CHLA DC IPs before running.

2.2 Create dACL in ISE

DACL_NAME="DACL_LINUX_RESEARCH_AD_AUTH"

netapi ise create-dacl "$DACL_NAME" \
  --file /tmp/dacl-linux-ad-auth-chla.txt \
  --descr "Linux 802.1X with AD-authenticated SSH - Xianming request"

2.3 Verify dACL

netapi ise get-dacl "$DACL_NAME"

2.4 Create Authorization Profile

AUTHZ_PROFILE="Linux_Research_AD_Auth"

netapi ise create-authz-profile "$AUTHZ_PROFILE" \
  --dacl "$DACL_NAME" \
  --descr "Linux EAP-TLS with AD SSH access - Xianming request"

2.5 Verify Authorization Profile

netapi ise get-authz-profile "$AUTHZ_PROFILE"

2.6 Add Authorization Rule

POLICY_SET="Wired Dot1X Closed"

# Dry run first
netapi ise add-authz-rule "$POLICY_SET" \
  "$AUTHZ_PROFILE" \
  "$AUTHZ_PROFILE" \
  --rank 0 \
  --dry-run
# Apply the rule
netapi ise add-authz-rule "$POLICY_SET" \
  "$AUTHZ_PROFILE" \
  "$AUTHZ_PROFILE" \
  --rank 0

2.7 Verify Authorization Rules

netapi ise get-authz-rules "$POLICY_SET"

Phase 3: Apply Policy and Reauthenticate

3.1 Issue CoA

MAC="<MAC-ADDRESS>"

netapi ise mnt coa "$MAC"

echo "Waiting 15 seconds for reauthentication..."
sleep 15

3.2 Verify New Session

netapi ise mnt session "$MAC"

Expected: Authorization profile = Linux_Research_AD_Auth

3.3 DataConnect Session View

netapi ise dc session "$MAC"

Phase 4: Endpoint Validation

4.1 AD Connectivity (AFTER dACL change)

From the target workstation:

DC_IP="<DC-IP-1>"

echo "=== AD Connectivity Post-dACL ==="
{
  echo -n "DNS (53)....... "; timeout 3 bash -c "</dev/tcp/$DC_IP/53" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "Kerberos (88).. "; timeout 3 bash -c "</dev/tcp/$DC_IP/88" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "LDAP (389)..... "; timeout 3 bash -c "</dev/tcp/$DC_IP/389" 2>/dev/null && echo "OK" || echo "FAIL"
  echo -n "SMB (445)...... "; timeout 3 bash -c "</dev/tcp/$DC_IP/445" 2>/dev/null && echo "OK" || echo "FAIL"
} 2>&1

Expected: All should now show OK

4.2 Kerberos Ticket Acquisition

# Get Kerberos ticket
kinit <user>@LA.AD.CHLA.ORG

# Verify ticket
klist

4.3 SSH with AD Account

From another machine, SSH to the target with AD credentials:

ssh <user>@la.ad.chla.org@<TARGET-IP> whoami

Expected: Login successful, returns domain username

4.4 Lateral Movement Block Test

echo "=== Lateral Movement Test (should FAIL) ==="

# These should timeout/fail
ping -c 2 10.112.142.1  # Internal server
ping -c 2 10.134.144.1  # Another internal server

echo "If pings succeeded, dACL is NOT working correctly!"

4.5 Internet Egress Test

echo "=== Internet Egress (should SUCCEED) ==="
curl -s -o /dev/null -w "HTTP %{http_code}\n" https://www.google.com

Expected: HTTP 200 or 301