TACACS+ Views
Overview
The TACACS+ views contain device administration authentication and command authorization audit records.
Schema
TACACS_AUTHENTICATION
| Column | Type | Description |
|---|---|---|
GENERATED_TIME |
TIMESTAMP |
Authentication timestamp |
USERNAME |
VARCHAR2 |
Admin username |
REMOTE_ADDRESS |
VARCHAR2 |
Source IP of admin |
DEVICE_IP |
VARCHAR2 |
Managed device IP |
DEVICE_NAME |
VARCHAR2 |
Device hostname |
STATUS |
VARCHAR2 |
'Pass' or 'Fail' |
FAILURE_REASON |
VARCHAR2 |
Detailed failure reason |
AUTHENTICATION_TYPE |
VARCHAR2 |
PAP, CHAP, ASCII |
TACACS_AUTHORIZATION
| Column | Type | Description |
|---|---|---|
GENERATED_TIME |
TIMESTAMP |
Authorization timestamp |
USERNAME |
VARCHAR2 |
Admin username |
DEVICE_IP |
VARCHAR2 |
Managed device IP |
DEVICE_NAME |
VARCHAR2 |
Device hostname |
CMD |
VARCHAR2 |
Executed command |
CMD_ARG |
VARCHAR2 |
Command arguments |
STATUS |
VARCHAR2 |
'Pass' or 'Fail' |
PRIVILEGE_LEVEL |
NUMBER |
TACACS privilege level (0-15) |
Authentication Queries
Recent Device Logins
# Recent device admin logins
netapi ise dc --format json query "
SELECT
TO_CHAR(GENERATED_TIME, 'HH24:MI:SS') as time,
USERNAME as admin,
DEVICE_NAME as device,
DEVICE_IP as device_ip,
REMOTE_ADDRESS as source_ip,
STATUS as status
FROM TACACS_AUTHENTICATION
WHERE GENERATED_TIME > SYSDATE - 1
ORDER BY GENERATED_TIME DESC
FETCH FIRST 30 ROWS ONLY
" | jq -r '
"\u001b[1m Time │ Admin │ Device │ Device IP │ Source\u001b[0m",
"───────────┼─────────────────┼──────────────────────┼──────────────┼─────────────",
(.[] |
(if .status == "Pass" then "\u001b[32m✓\u001b[0m" else "\u001b[31m✗\u001b[0m" end) +
" " + (.time // " ") + " │ " +
"\u001b[33m" + (((.admin // "") + " ") | .[0:15]) + "\u001b[0m │ " +
"\u001b[36m" + (((.device // "") + " ") | .[0:20]) + "\u001b[0m │ " +
(((.device_ip // "") + " ") | .[0:12]) + " │ " + (.source_ip // "")
)
'
Failed Login Analysis
# Failed TACACS authentications (potential brute force)
netapi ise dc --format json query "
SELECT
USERNAME as admin,
REMOTE_ADDRESS as source_ip,
COUNT(*) as failures,
COUNT(DISTINCT DEVICE_IP) as target_devices,
MIN(TO_CHAR(GENERATED_TIME, 'HH24:MI')) as first_attempt,
MAX(TO_CHAR(GENERATED_TIME, 'HH24:MI')) as last_attempt
FROM TACACS_AUTHENTICATION
WHERE STATUS = 'Fail'
AND GENERATED_TIME > SYSDATE - 1
GROUP BY USERNAME, REMOTE_ADDRESS
HAVING COUNT(*) >= 3
ORDER BY failures DESC
" | jq -r '
if length == 0 then
"\u001b[32m✓ No suspicious login patterns detected\u001b[0m"
else
"\u001b[31m⚠ Suspicious login activity:\u001b[0m",
(.[] |
"\u001b[31m●\u001b[0m " +
"\u001b[33m" + .admin + "\u001b[0m from " + .source_ip +
" │ " + (.failures | tostring) + " failures │ " +
(.target_devices | tostring) + " devices │ " +
.first_attempt + "-" + .last_attempt
)
end
'
Command Authorization
Command Audit Trail
# Command authorization audit trail
netapi ise dc --format json query "
SELECT
TO_CHAR(GENERATED_TIME, 'HH24:MI:SS') as time,
USERNAME as admin,
DEVICE_NAME as device,
CMD as command,
CMD_ARG as args,
STATUS as status
FROM TACACS_COMMAND_VIEW
WHERE GENERATED_TIME > SYSDATE - 1
AND CMD IS NOT NULL
ORDER BY GENERATED_TIME DESC
FETCH FIRST 50 ROWS ONLY
" | jq -r '
"\u001b[1m Time │ Admin │ Device │ Command\u001b[0m",
"───────────┼────────────┼─────────────────┼─────────────────────────",
(.[] |
(if .status == "Pass" then "\u001b[32m✓\u001b[0m" else "\u001b[31m✗\u001b[0m" end) +
" " + (.time // " ") + " │ " +
"\u001b[33m" + (((.admin // "") + " ") | .[0:10]) + "\u001b[0m │ " +
"\u001b[36m" + (((.device // "") + " ") | .[0:15]) + "\u001b[0m │ " +
(.command // "") + " " + (.args // "")
)
'
Sensitive Commands
# Sensitive/dangerous commands executed
netapi ise dc --format json query "
SELECT
TO_CHAR(GENERATED_TIME, 'YYYY-MM-DD HH24:MI:SS') as timestamp,
USERNAME as admin,
DEVICE_NAME as device,
DEVICE_IP as device_ip,
CMD as command,
CMD_ARG as args,
STATUS as status
FROM TACACS_COMMAND_VIEW
WHERE (
LOWER(CMD) LIKE '%write%'
OR LOWER(CMD) LIKE '%copy%'
OR LOWER(CMD) LIKE '%reload%'
OR LOWER(CMD) LIKE '%delete%'
OR LOWER(CMD) LIKE '%erase%'
OR LOWER(CMD) LIKE '%debug%'
OR LOWER(CMD) LIKE '%crypto%'
OR LOWER(CMD) LIKE '%enable%'
OR LOWER(CMD) LIKE '%username%'
OR LOWER(CMD) LIKE '%aaa%'
)
AND GENERATED_TIME > SYSDATE - 7
ORDER BY GENERATED_TIME DESC
FETCH FIRST 100 ROWS ONLY
" | jq -r '
.[] |
(if .status == "Pass" then "\u001b[33m⚠\u001b[0m" else "\u001b[31m✗\u001b[0m" end) +
" " + .timestamp + " │ " +
"\u001b[33m" + .admin + "\u001b[0m │ " +
"\u001b[36m" + .device + "\u001b[0m │ " +
"\u001b[35m" + .command + " " + (.args // "") + "\u001b[0m"
'
Admin Analysis
Activity Summary
# Admin activity summary (last 24h)
netapi ise dc --format json query "
SELECT
a.USERNAME as admin,
COUNT(DISTINCT a.DEVICE_IP) as devices_accessed,
COUNT(*) as login_count,
SUM(CASE WHEN a.STATUS = 'Fail' THEN 1 ELSE 0 END) as failed_logins
FROM TACACS_AUTHENTICATION a
WHERE a.GENERATED_TIME > SYSDATE - 1
GROUP BY a.USERNAME
ORDER BY login_count DESC
" | jq -r '
"┌──────────────────┬──────────┬─────────┬─────────┐",
"│ Admin │ Devices │ Logins │ Failed │",
"├──────────────────┼──────────┼─────────┼─────────┤",
(.[] |
"│ \u001b[33m" + (((.admin // "") + " ") | .[0:16]) + "\u001b[0m │" +
((" " + ((.devices_accessed // 0)|tostring))[-8:]) + " │" +
((" " + ((.login_count // 0)|tostring))[-7:]) + " │" +
(if (.failed_logins // 0) > 0 then "\u001b[31m" else "" end) +
((" " + ((.failed_logins // 0)|tostring))[-7:]) +
(if (.failed_logins // 0) > 0 then "\u001b[0m" else "" end) + " │"
),
"└──────────────────┴──────────┴─────────┴─────────┘"
'
Device Access Matrix
# Device access matrix (who accessed what)
netapi ise dc --format json query "
SELECT
DEVICE_NAME as device,
USERNAME as admin,
COUNT(*) as sessions,
MAX(TO_CHAR(GENERATED_TIME, 'HH24:MI')) as last_access
FROM TACACS_AUTHENTICATION
WHERE STATUS = 'Pass'
AND GENERATED_TIME > SYSDATE - 1
GROUP BY DEVICE_NAME, USERNAME
ORDER BY device, sessions DESC
" | jq -r '
group_by(.device) | .[] |
"\u001b[1m\u001b[36m" + .[0].device + "\u001b[0m",
(sort_by(-.sessions) | .[] |
" \u001b[33m" + .admin + "\u001b[0m │ " +
(.sessions | tostring) + " sessions │ last: " + .last_access
),
""
'
Security Analysis
Off-Hours Activity
# Off-hours device access (potential unauthorized)
netapi ise dc --format json query "
SELECT
TO_CHAR(GENERATED_TIME, 'YYYY-MM-DD HH24:MI') as timestamp,
USERNAME as admin,
DEVICE_NAME as device,
REMOTE_ADDRESS as source_ip,
STATUS as status
FROM TACACS_AUTHENTICATION
WHERE (
TO_CHAR(GENERATED_TIME, 'HH24') < '06'
OR TO_CHAR(GENERATED_TIME, 'HH24') >= '22'
OR TO_CHAR(GENERATED_TIME, 'DY', 'NLS_DATE_LANGUAGE=ENGLISH') IN ('SAT', 'SUN')
)
AND GENERATED_TIME > SYSDATE - 7
ORDER BY GENERATED_TIME DESC
FETCH FIRST 50 ROWS ONLY
" | jq -r '
if length == 0 then
"\u001b[32m✓ No off-hours access detected\u001b[0m"
else
"\u001b[33m⚠ Off-hours device access:\u001b[0m",
(.[] |
(if .status == "Pass" then "\u001b[33m●\u001b[0m" else "\u001b[31m●\u001b[0m" end) +
" " + .timestamp + " │ " +
"\u001b[33m" + .admin + "\u001b[0m │ " +
"\u001b[36m" + .device + "\u001b[0m │ from " + .source_ip
)
end
'
Export
CSV Export
# Export TACACS audit trail to CSV
# Note: netapi dc doesn't have --format csv, use JSON + jq conversion
netapi ise dc --format json query "
SELECT
'Authentication' as record_type,
TO_CHAR(a.GENERATED_TIME, 'YYYY-MM-DD HH24:MI:SS') as timestamp,
a.USERNAME as admin,
a.DEVICE_NAME as device,
a.DEVICE_IP as device_ip,
a.REMOTE_ADDRESS as source_ip,
NULL as command,
a.STATUS as status,
a.FAILURE_REASON as details
FROM TACACS_AUTHENTICATION a
WHERE a.GENERATED_TIME > SYSDATE - 7
UNION ALL
SELECT
'Command' as record_type,
TO_CHAR(c.GENERATED_TIME, 'YYYY-MM-DD HH24:MI:SS') as timestamp,
c.USERNAME as admin,
c.DEVICE_NAME as device,
c.DEVICE_IP as device_ip,
NULL as source_ip,
c.CMD || ' ' || NVL(c.CMD_ARG, '') as command,
c.STATUS as status,
NULL as details
FROM TACACS_COMMAND_VIEW c
WHERE c.GENERATED_TIME > SYSDATE - 7
ORDER BY timestamp DESC
" | jq -r '
(.[0] | keys) as $k |
($k | @csv),
(.[] | [.[$k[]]] | @csv)
' > /tmp/tacacs-audit-$(date +%Y%m%d).csv
echo "Exported $(wc -l < /tmp/tacacs-audit-$(date +%Y%m%d).csv) TACACS records"