ci / audit (push) Successful in 20s
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
90 lines
4.0 KiB
Bash
Executable File
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
|