Files
esh-pfi-infrastructure/scripts/discover-fortigate.sh
T
vh 921891b27a scripts: network-discovery tooling for FortiGate + UniFi
Three scripts that surface hosts on the fleet's networks that aren't
already tracked under servers/*/. Goal: spot servers that need management
coverage (inventory, backup, monitoring) without wandering the LAN by
hand.

  discover-fortigate.sh  SSH to a FortiGate admin, run
                         `execute dhcp lease-list all`, emit TSV
                         (IP, MAC, hostname, vdom, source).

                         SSH was picked over the REST API for now
                         because it needs no API-token plumbing. The
                         parser is defensive about FortiOS output
                         format drift (multiple VDOM sections,
                         optional hostname). API variant can replace
                         it when the extra robustness is worth the
                         token setup.

  discover-unifi.sh      Cookie-auth REST call against a UniFi
                         Controller. Tries /api/auth/login (UniFi OS
                         / UDM / Cloud Key Gen2+) first; falls back
                         to legacy /api/login for self-hosted
                         controllers. Output is the same TSV shape
                         as the FortiGate script so the two mix.

                         Needs UNIFI_USER / UNIFI_PASS env and jq.

  discover-gaps.sh       Consumes one or more TSVs from the sources
                         above. Builds the set of managed IPs from
                         servers/*/ssh-target (plus a grep of README
                         files for documented IPs) and prints any
                         discovered IPs not in that set.

                         Exit code is 1 if gaps found — suitable for
                         cron alerting.

Common pipeline:
  scripts/discover-fortigate.sh ana-fw.phasefinal.com > /tmp/ana.tsv
  scripts/discover-fortigate.sh nh3-gw.phasefinal.com > /tmp/nh3.tsv
  UNIFI_USER=admin UNIFI_PASS=… scripts/discover-unifi.sh esh-uc.esteban.net > /tmp/esh.tsv
  scripts/discover-gaps.sh /tmp/ana.tsv /tmp/nh3.tsv /tmp/esh.tsv

First-time use probably needs:
  - SSH access configured to each FortiGate (admin login, key preferred)
  - UniFi user with read access (the built-in API read-only role works)
  - `jq` installed on the runner (for UniFi script)
2026-04-21 11:51:00 -07:00

79 lines
2.7 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.
# Send via stdin so we don't rely on interactive shell handling.
raw=$(ssh -o BatchMode=no -o StrictHostKeyChecking=accept-new \
"${USER}@${HOST}" 'execute dhcp lease-list all' 2>/dev/null \
|| ssh -o BatchMode=no -o StrictHostKeyChecking=accept-new \
"${USER}@${HOST}" 'execute dhcp lease-list' 2>/dev/null)
if [ -z "$raw" ]; then
echo "error: no lease data from $HOST (check SSH access / admin creds)" >&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"