Files
esh-pfi-infrastructure/scripts/discover-fortigate.sh
T
vh 83eddc872a scripts/discover-fortigate: stop hiding SSH errors
First real run returned empty and we had no idea why — the script was
silently swallowing stderr via `2>/dev/null`. Remove the suppression
and try both `execute dhcp lease-list all` and the no-arg form, keeping
whichever returns non-error output.

Also emit a clearer diagnostic when both fail, pointing the user at
an interactive SSH to poke at command syntax.
2026-04-21 13:35:53 -07:00

87 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# discover-fortigate.sh — pull DHCP lease list from a FortiGate.
#
# SSHes to a FortiGate admin account, runs `execute dhcp lease-list`,
# parses the output into TSV (IP, MAC, hostname, vdom, source).
#
# Usage:
# scripts/discover-fortigate.sh <fortigate-host>
#
# Env overrides:
# FORTIGATE_SSH_USER default: admin
#
# Output: TSV on stdout, one lease per line:
# IP MAC HOSTNAME VDOM SOURCE
# Where SOURCE is "fortigate:<host>" so multiple runs can be concatenated
# and still identified.
#
# Example:
# scripts/discover-fortigate.sh ana-fw.phasefinal.com > leases-ana.tsv
# scripts/discover-fortigate.sh nh3-gw.phasefinal.com > leases-nh3.tsv
#
# Requires: ssh config (or `~/.ssh/config` host alias) for the FortiGate,
# with key auth OR interactive password. FortiGate's SSH expects admin-level
# credentials.
set -euo pipefail
if [ -z "${1:-}" ]; then
echo "usage: $(basename "$0") <fortigate-host>" >&2
exit 2
fi
HOST="$1"
USER="${FORTIGATE_SSH_USER:-admin}"
# FortiGate CLI command. `execute dhcp lease-list all` dumps every vdom.
# If the device is single-vdom, `execute dhcp lease-list` (no arg) also works.
#
# Do NOT suppress stderr — FortiGate's error messages are the main debugging
# signal when the command returns empty. Let them flow to the caller.
raw=""
for cmd in 'execute dhcp lease-list all' 'execute dhcp lease-list'; do
raw=$(ssh -o StrictHostKeyChecking=accept-new "${USER}@${HOST}" "$cmd" || true)
if [ -n "$raw" ] && ! grep -qiE 'unknown action|parse error|command fail' <<<"$raw"; then
break
fi
done
if [ -z "$raw" ]; then
echo "error: no lease data from $HOST" >&2
echo " — try running 'ssh ${USER}@${HOST}' interactively and running" >&2
echo " 'execute dhcp lease-list ?' to discover the right subcommand shape" >&2
exit 1
fi
# Parse.
#
# FortiGate lease output looks approximately like:
#
# VDOM: root
# Interface: internal
# IP MAC Hostname Lease-Expiry
# 10.250.50.100 00:1a:2b:3c:4d:5e laptop-01 Mon Apr 21 14:30:00 2026
# 10.250.50.101 aa:bb:cc:dd:ee:ff printer Mon Apr 21 15:00:00 2026
#
# Format varies by FortiOS version. The awk below is defensive:
# - only accepts lines where field 1 matches IPv4
# - assumes MAC is field 2, hostname is field 3-4 (may contain whitespace),
# which is the most common layout.
awk -v src="fortigate:${HOST}" -v vdom="" '
/^VDOM:/ { vdom=$2; next }
/^Interface:/ { next }
/^IP[[:space:]]/ { next }
/^[[:space:]]*$/ { next }
$1 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {
ip = $1
mac = $2
# Hostname may be missing (shows "-" or empty) or contain spaces; take
# the rest of the line minus the trailing date fields.
host = ""
for (i=3; i<=NF-6; i++) host = host (host=="" ? "" : " ") $i
if (host == "" || host == "-") host = "-"
printf "%s\t%s\t%s\t%s\t%s\n", ip, mac, host, (vdom==""?"-":vdom), src
}
' <<<"$raw"