Files
compliance/openvas/glpi-to-openvas.sh
T
mrcharles 8eab675c74
ci / audit (push) Successful in 20s
[#389] GLPI→OpenVAS feed pilot: script (fixture-tested, 63 targets) + GVM app-test compose + pipeline docs
Feed reads GLPI computers read-only (mglpi identity), parses ts=/mgmt IP
comment convention, emits CSV or GMP create_target fragments. Dual
shellcheck green. GVM stack = Greenbone community containers, loopback.
https://projects.knownelement.com/issues/389
2026-09-05 06:56:00 -05:00

90 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# glpi-to-openvas.sh — feed OpenVAS/GVM scan targets from the GLPI CMDB [#389]
#
# Pulls the computer inventory from GLPI (REST, read-only) and emits:
# --format csv id,name,ts_ip,mgmt_ip,serial,last_inventory (audit/review)
# --format gmp <create_target> XML fragments ready for gvm-tools/GMP
#
# GLPI convention: IPs live in the computer comment field as
# "ts=<tailscale-ip> | mgmt <lan-ip> | src=..." (mixed = and space, see KNEL/inventory seed)
# Records without any parsable IP are skipped unless --include-no-ip.
#
# Usage:
# glpi-to-openvas.sh --format gmp [--env-file ~/.creds/mglpi.env] [--range 0-499]
# glpi-to-openvas.sh --format csv --fixture FILE.json # offline transform test
#
# The GMP output is imported with e.g.:
# gvm-tools script --gmp-username user --gmp-password pass feed.gmp
# Creds: GLPI tokens come from the env file (never on the command line).
set -uo pipefail
FORMAT="csv"; ENV_FILE="${MGLPI_ENV:-$HOME/.creds/mglpi.env}"; RANGE="0-499"
FIXTURE=""; INCLUDE_NO_IP=0; ONLY_DYNAMIC=0
while [ $# -gt 0 ]; do
case "$1" in
--format) FORMAT="$2"; shift 2 ;;
--env-file) ENV_FILE="$2"; shift 2 ;;
--range) RANGE="$2"; shift 2 ;;
--fixture) FIXTURE="$2"; shift 2 ;;
--include-no-ip) INCLUDE_NO_IP=1; shift ;;
--only-dynamic) ONLY_DYNAMIC=1; shift ;; # is_dynamic=1 only (agent-fed)
-h|--help) sed -n '2,20p' "${BASH_SOURCE[0]}" >&2; exit 0 ;;
*) echo "unknown arg: $1" >&2; exit 1 ;;
esac
done
fetch_inventory() {
# shellcheck disable=SC1090
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
: "${MGLPI_URL:?MGLPI_URL missing}"; : "${MGLPI_APP_TOKEN:?}"; : "${MGLPI_USER_TOKEN:?}"
local S
S=$(curl -sS --max-time 30 -H "Content-Type: application/json" \
-H "App-Token: ${MGLPI_APP_TOKEN}" -H "Authorization: user_token ${MGLPI_USER_TOKEN}" \
"${MGLPI_URL}/initSession" | jq -r '.session_token // empty')
[ -n "$S" ] || { echo "FAIL: GLPI session init" >&2; return 1; }
curl -sS --max-time 60 -H "App-Token: ${MGLPI_APP_TOKEN}" -H "Session-Token: $S" \
"${MGLPI_URL}/Computer?range=${RANGE}&is_deleted=0" \
| jq -c '[.[] | select(type == "object")]'
curl -sS --max-time 30 -X DELETE -H "App-Token: ${MGLPI_APP_TOKEN}" \
-H "Session-Token: $S" "${MGLPI_URL}/killSession" >/dev/null
}
INVENTORY_JSON="/tmp/glpi-computers.json"
if [ -n "$FIXTURE" ]; then
INVENTORY_JSON="$FIXTURE"
else
fetch_inventory > "$INVENTORY_JSON" || exit 1
fi
# Normalize: one record per line with parsed IPs
NORMALIZED=$(jq -r '
.[]
| select( ($ONLY_DYNAMIC == 0 or .is_dynamic == 1) )
| ( .comment // "" ) as $c
| ( [$c | scan("ts=([0-9.]+)")] | first | first // "" ) as $ts
| ( [$c | scan("mgmt[ =]([0-9.]+)")] | first | first // "" ) as $mgmt
| select( $INCLUDE_NO_IP == 1 or $ts != "" or $mgmt != "" )
| [ (.id|tostring), .name, $ts, $mgmt, (.serial // "-"),
(.last_inventory_update // "-"), ((.is_dynamic // 0)|tostring) ]
| @tsv' --argjson ONLY_DYNAMIC "$ONLY_DYNAMIC" --argjson INCLUDE_NO_IP "$INCLUDE_NO_IP" "$INVENTORY_JSON")
[ -n "$NORMALIZED" ] || { echo "no scan targets parsed from GLPI" >&2; exit 2; }
case "$FORMAT" in
csv)
printf 'glpi_id,name,ts_ip,mgmt_ip,serial,last_inventory,agent_fed\n'
printf '%s\n' "$NORMALIZED"
;;
gmp)
printf '<!-- GMP create_target fragments generated from GLPI CMDB %s. Review hosts before importing. -->\n' "$(date -Is)"
printf '%s\n' "$NORMALIZED" | while IFS="$(printf '\t')" read -r id name ts mgmt _serial inv dyn; do
hosts="$ts"
if [ -n "$mgmt" ]; then hosts="${hosts:+$hosts,}$mgmt"; fi
[ -n "$hosts" ] || continue
printf '<create_target><name>glpi-%s-%s</name><hosts>%s</hosts><comment>GLPI #%s inv=%s agent=%s</comment><alive_tests>Scan Config Default</alive_tests></create_target>\n' \
"$id" "$(printf '%s' "$name" | tr -c '[:alnum:].-' '-')" "$hosts" "$id" "$inv" "$dyn"
done
;;
*) echo "bad format: $FORMAT" >&2; exit 1 ;;
esac