Auth Method Deployment Workflow

Overview

This workflow covers the complete process for deploying new authentication methods:

  1. Phase 1: Pre-deployment validation (capture baseline)

  2. Phase 2: Configuration (allowed protocols, authz profiles, policy rules)

  3. Phase 3: Testing (single endpoint validation)

  4. Phase 4: Rollout (gradual with CoA)

  5. Phase 5: Post-deployment validation

  6. Rollback: If issues occur

Setup

dsource d000 dev/network

# ERS API (port 9060) - configuration
ISE_HOST="${ISE_PAN_IP}"
ISE_PORT="9060"
ISE_AUTH="${ISE_API_USER}:${ISE_API_PASS}"
BASE_URL="https://${ISE_HOST}:${ISE_PORT}/ers/config"

# OpenAPI (port 443) - policy management
POLICY_API="https://${ISE_HOST}/api/v1/policy/network-access"

Phase 1: Pre-Deployment Validation

Capture Baseline

# === PHASE 1: Capture baseline before changes ===

# 1.1 Current auth method distribution
echo "=== Auth Method Distribution (Last 24h) ==="
netapi ise dc --format json query "
  SELECT
    authentication_method as method,
    COUNT(*) as total,
    SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) as passed,
    ROUND(SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) as pct
  FROM mnt.radius_auth_48_live
  WHERE acs_timestamp > SYSDATE - 1
  GROUP BY authentication_method
  ORDER BY total DESC
" | jq -r '
  .[] | .method + " │ " + (.total|tostring) + " │ " + (.pct|tostring) + "%"
'

# 1.2 Current policy sets and allowed protocols
echo -e "\n=== Policy Sets ==="
netapi ise get-policy-sets | jq -r '.[] | .name + " │ " + .id'

# 1.3 Current allowed protocols
echo -e "\n=== Allowed Protocols Services ==="
netapi ise ers allowed-protocols | jq -r '.[] | .name'

# Save baseline for comparison
netapi ise dc --format json query "
  SELECT authentication_method, COUNT(*) as count
  FROM mnt.radius_auth_48_live
  WHERE acs_timestamp > SYSDATE - 1
  GROUP BY authentication_method
" --format json > /tmp/auth-baseline-$(date +%Y%m%d).json
echo "Baseline saved to /tmp/auth-baseline-$(date +%Y%m%d).json"

Check Certificates

# 1.4 Check ISE trusted certificates (needed for EAP-TLS/TEAP)
echo "=== ISE Trusted Certificates ==="
netapi ise api-call openapi GET '/api/v1/certs/trusted-certificate?size=100' | jq -r '
  .response[] |
  select(.trustedFor | contains("INFRASTRUCTURE") or contains("CLIENT")) |
  "\u001b[32m✓\u001b[0m " + .friendlyName + " │ Expires: " + .expirationDate
'

# Check if your CA is trusted
CA_NAME="DOMUS"
echo -e "\n=== Checking for $CA_NAME CA ==="
netapi ise api-call openapi GET '/api/v1/certs/trusted-certificate?size=100' | jq -r "
  .response[] | select(.friendlyName | test(\"$CA_NAME\"; \"i\")) |
  \"Found: \" + .friendlyName + \" │ \" + .status
"

Phase 2: Configuration

Configure Allowed Protocols

# === PHASE 2: Configure Allowed Protocols ===

# 2.1 Create new allowed protocols (or modify existing)
# Option A: Create EAP-TLS only (most secure)
curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/allowedprotocols" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d '{
    "AllowedProtocols": {
      "name": "Wired-EAP-TLS-Only",
      "description": "Production: Certificate-only authentication",
      "allowEapTls": true,
      "allowPeap": false,
      "allowTeap": false,
      "allowEapFast": false,
      "allowPapAscii": false,
      "allowChap": false,
      "allowMsChapV2": false,
      "allowLeap": false,
      "processHostLookup": false,
      "requireCryptobinding": true,
      "allowWeakCiphersForEap": false
    }
  }' | jq -r '.AllowedProtocols | "Created: " + .name + " │ ID: " + .id'

# 2.2 Verify creation
netapi ise ers allowed-protocols --name "Wired-EAP-TLS-Only" | jq '{name, allowEapTls, allowPeap, allowTeap}'

Create Authorization Profile

# 2.3 Create authorization profile with dACL
DACL_NAME="Linux-AD-Auth-Hardened"
PROFILE_NAME="Linux-EAP-TLS-Hardened"

# First create the dACL (if not exists)
curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/downloadableacl" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d "{
    \"DownloadableAcl\": {
      \"name\": \"${DACL_NAME}\",
      \"description\": \"Zero-trust: AD authentication only\",
      \"dacl\": \"permit udp any any eq 53\\npermit tcp any any eq 88\\npermit udp any any eq 88\\npermit tcp any any eq 389\\npermit tcp any any eq 636\\npermit tcp any any eq 445\\npermit udp any any eq 123\\ndeny ip any any\"
    }
  }" | jq -r '.DownloadableAcl | "Created dACL: " + .name'

# Then create the authz profile
curl -sk -u "${ISE_AUTH}" \
  "${BASE_URL}/authorizationprofile" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d "{
    \"AuthorizationProfile\": {
      \"name\": \"${PROFILE_NAME}\",
      \"description\": \"EAP-TLS authenticated Linux workstation\",
      \"accessType\": \"ACCESS_ACCEPT\",
      \"daclName\": \"${DACL_NAME}\",
      \"vlan\": {
        \"nameID\": \"VLAN10\"
      }
    }
  }" | jq -r '.AuthorizationProfile | "Created Profile: " + .name'

Create Policy Rules

# 2.4 Create authentication and authorization rules
POLICY_SET_ID=$(netapi ise get-policy-sets | jq -r '.[] | select(.name == "Wired_802.1X") | .id')
echo "Policy Set ID: $POLICY_SET_ID"

# Create auth rule for EAP-TLS
curl -sk -u "${ISE_AUTH}" \
  "${POLICY_API}/policy-set/${POLICY_SET_ID}/authentication" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d '{
    "rule": {
      "name": "EAP-TLS-Certificate-Auth",
      "state": "enabled",
      "condition": {
        "conditionType": "ConditionAttributes",
        "dictionaryName": "Network Access",
        "attributeName": "EapAuthentication",
        "operator": "equals",
        "attributeValue": "EAP-TLS"
      },
      "rank": 1
    },
    "identitySourceName": "Internal Endpoints",
    "ifAuthFail": "REJECT",
    "ifUserNotFound": "REJECT",
    "ifProcessFail": "DROP"
  }' | jq -r '.response.rule | "Created auth rule: " + .name'

# Create authz rule
curl -sk -u "${ISE_AUTH}" \
  "${POLICY_API}/policy-set/${POLICY_SET_ID}/authorization" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -X POST \
  -d '{
    "rule": {
      "name": "EAP-TLS-Full-Access",
      "state": "enabled",
      "condition": {
        "conditionType": "ConditionAttributes",
        "dictionaryName": "Network Access",
        "attributeName": "EapAuthentication",
        "operator": "equals",
        "attributeValue": "EAP-TLS"
      },
      "rank": 1
    },
    "profile": ["PermitAccess"]
  }' | jq -r '.response.rule | "Created authz rule: " + .name'

Phase 3: Testing

Test Single Endpoint

# === PHASE 3: Test with single endpoint ===

# 3.1 Register test endpoint
TEST_MAC="AA:BB:CC:DD:EE:FF"
netapi ise create-endpoint "$TEST_MAC" --group "Linux-Workstations" --description "Test endpoint"

# 3.2 Attempt authentication from test device
# (Run from test endpoint, not here)
# wpa_supplicant or nmcli connection

# 3.3 Check authentication result
echo "=== Checking auth result for $TEST_MAC ==="
netapi ise dc --format json query "
  SELECT
    TO_CHAR(acs_timestamp, 'HH24:MI:SS') as time,
    authentication_method as method,
    CASE WHEN passed = 1 THEN 'PASS' ELSE 'FAIL' END as status,
    selected_azn_profiles as profile,
    failure_reason
  FROM mnt.radius_auth_48_live
  WHERE calling_station_id = '${TEST_MAC}'
  AND acs_timestamp > SYSDATE - 1/24
  ORDER BY acs_timestamp DESC
  FETCH FIRST 5 ROWS ONLY
" | jq -r '
  .[] |
  (if .status == "PASS" then "\u001b[32m✓\u001b[0m" else "\u001b[31m✗\u001b[0m" end) +
  " " + .time + " │ " + .method + " │ " + .status + " │ " +
  (.profile // "N/A") + " │ " + (.failure_reason // "-")
'

# 3.4 Check active session
netapi ise mnt sessions | jq '.[] | select(.calling_station_id == "'$TEST_MAC'")'

Troubleshoot Failures

# 3.5 Troubleshoot if failed
TEST_MAC="AA:BB:CC:DD:EE:FF"

# Get detailed failure info
netapi ise dc --format json query "
  SELECT
    TO_CHAR(acs_timestamp, 'YYYY-MM-DD HH24:MI:SS') as timestamp,
    failure_reason,
    nas_ip_address as nas,
    authentication_method as method
  FROM mnt.radius_auth_48_live
  WHERE calling_station_id = '${TEST_MAC}'
  AND passed = 0
  ORDER BY acs_timestamp DESC
  FETCH FIRST 10 ROWS ONLY
" | jq '.'

# Common failure reasons and fixes:
# - "EAP-TLS failed SSL/TLS handshake" → Check cert chain, CA trust
# - "Could not locate user" → Check endpoint exists in ISE
# - "Authentication method not supported" → Check allowed protocols

Phase 4: Production Rollout

# === PHASE 4: Production rollout ===

# 4.1 Push CoA to re-authenticate endpoints (gradual rollout)
# Start with a small group
netapi ise dc --format json query "
  SELECT DISTINCT calling_station_id as mac
  FROM mnt.radius_auth_48_live
  WHERE selected_azn_profiles = 'Old-Profile'
  AND acs_timestamp > SYSDATE - 1
  FETCH FIRST 10 ROWS ONLY
" | jq -r '.[].mac' | while read mac; do
  echo "Pushing CoA to $mac"
  netapi ise mnt coa --mac "$mac" --action reauth
  sleep 2
done

# 4.2 Monitor success rate during rollout
watch -n 30 'netapi ise dc --format json query "
  SELECT
    authentication_method,
    ROUND(SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) as success_pct,
    COUNT(*) as total
  FROM mnt.radius_auth_48_live
  WHERE acs_timestamp > SYSDATE - 1/24
  GROUP BY authentication_method
  ORDER BY total DESC
" | jq -r ".[] | .authentication_method + \": \" + (.success_pct|tostring) + \"%\""'

Phase 5: Post-Deployment Validation

# === PHASE 5: Post-deployment validation ===

# 5.1 Compare to baseline
echo "=== Before vs After Comparison ==="
BASELINE=$(cat /tmp/auth-baseline-$(date +%Y%m%d).json)
CURRENT=$(netapi ise dc --format json query "
  SELECT authentication_method, COUNT(*) as count
  FROM mnt.radius_auth_48_live
  WHERE acs_timestamp > SYSDATE - 1
  GROUP BY authentication_method
" --format json)

echo "$BASELINE" | jq -r '.[] | "BEFORE: " + .authentication_method + " = " + (.count|tostring)'
echo "---"
echo "$CURRENT" | jq -r '.[] | "AFTER:  " + .authentication_method + " = " + (.count|tostring)'

# 5.2 Check for failures with new method
echo -e "\n=== EAP-TLS Failures (if any) ==="
netapi ise dc --format json query "
  SELECT
    failure_reason,
    COUNT(*) as count
  FROM mnt.radius_auth_48_live
  WHERE authentication_method = 'EAP-TLS'
  AND passed = 0
  AND acs_timestamp > SYSDATE - 1
  GROUP BY failure_reason
  ORDER BY count DESC
" | jq -r '
  if length == 0 then "\u001b[32m✓ No EAP-TLS failures\u001b[0m"
  else .[] | "\u001b[31m✗\u001b[0m " + .failure_reason + " (" + (.count|tostring) + ")"
  end
'

# 5.3 Overall success rate
echo -e "\n=== Overall Success Rate ==="
netapi ise dc --format json query "
  SELECT
    ROUND(SUM(CASE WHEN passed = 1 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as success_pct,
    COUNT(*) as total_auths
  FROM mnt.radius_auth_48_live
  WHERE acs_timestamp > SYSDATE - 1
" | jq -r '
  .[] |
  (if .success_pct >= 98 then "\u001b[32m" elif .success_pct >= 95 then "\u001b[33m" else "\u001b[31m" end) +
  "Success Rate: " + (.success_pct|tostring) + "% (" + (.total_auths|tostring) + " auths)\u001b[0m"
'

Rollback Procedures

# === ROLLBACK: If issues occur ===

# Option 1: Disable new auth rule (keeps config, stops matching)
POLICY_SET_ID="your-policy-set-id"
RULE_ID="your-new-rule-id"
curl -sk -u "${ISE_AUTH}" \
  "${POLICY_API}/policy-set/${POLICY_SET_ID}/authentication/${RULE_ID}" \
  -H "Accept: application/json" | jq '.response | .rule.state = "disabled"' > /tmp/disable-rule.json
curl -sk -u "${ISE_AUTH}" \
  "${POLICY_API}/policy-set/${POLICY_SET_ID}/authentication/${RULE_ID}" \
  -H "Content-Type: application/json" -X PUT -d @/tmp/disable-rule.json

# Option 2: Re-enable old allowed protocols on policy set
# (Update policy set to use old allowed protocols service)

# Option 3: Push CoA to force re-authentication with old method
netapi ise mnt coa --nas "10.50.1.200" --action reauth-all