#!/usr/bin/env bash # Netmaker CI helper: bring WireGuard up/down and manage ephemeral client lifecycle. # Subcommands: # up - fetch config, capture Client-ID, bring interface up, save state # down - bring interface down, delete local conf, delete client via API # # Env vars (can be overridden by flags): # NETMAKER_BASE_URL (required) e.g. https://nm.example.com or pass --base-url # NETMAKER_NETWORK (required) e.g. corpnet or pass --network # NETMAKER_API_JWT (required) Bearer token or pass --jwt # WG_IFACE (default netmaker) or pass --iface # WG_CONF_DIR (default /etc/wireguard) or pass --confdir # NETMAKER_STATE_FILE (default RUNNER_TEMP or /tmp) # You may also pass --client-id on `down` to avoid relying on the state file. set -euo pipefail # ---------- defaults ---------- WG_IFACE="${WG_IFACE:-netmaker}" WG_CONF_DIR="${WG_CONF_DIR:-/etc/wireguard}" SUBCMD="" CLIENT_ID_OVERRIDE="" usage() { cat <&2; usage; exit 2;; esac done STATE_FILE="${NETMAKER_STATE_FILE:-${RUNNER_TEMP:-/tmp}/netmaker_ci_${WG_IFACE}.env}" require_env() { : "${NETMAKER_BASE_URL:?ERROR: NETMAKER_BASE_URL not set}" : "${NETMAKER_NETWORK:?ERROR: NETMAKER_NETWORK not set}" : "${NETMAKER_API_JWT:?ERROR: NETMAKER_API_JWT not set}" } install_deps() { echo "[*] Checking dependencies ..." local need=(curl jq wg-quick ip) local miss=() for b in "${need[@]}"; do command -v "$b" >/dev/null 2>&1 || miss+=("$b"); done if [[ ${#miss[@]} -eq 0 ]]; then echo "[*] All dependencies present." return fi echo "[*] Installing missing deps: ${miss[*]}" if command -v apt-get >/dev/null 2>&1; then sudo apt-get update -y sudo apt-get install -y wireguard-tools jq curl iproute2 resolvconf elif command -v yum >/dev/null 2>&1; then sudo yum install -y wireguard-tools jq curl iproute iproute-tc elif command -v dnf >/dev/null 2>&1; then sudo dnf install -y wireguard-tools jq curl iproute else echo "ERROR: no supported package manager found; install: curl jq wireguard-tools iproute" >&2 exit 1 fi } do_up() { require_env install_deps local ep="${NETMAKER_BASE_URL}/api/v1/client_conf/${NETMAKER_NETWORK}" local tmp_conf="/tmp/${WG_IFACE}.conf" local tmp_hdr="/tmp/${WG_IFACE}.headers" echo "[*] Requesting client config: ${ep}" # Optional headers declare -a hdrs hdrs=(-H "Authorization: Bearer ${NETMAKER_API_JWT}") [[ -n "${NM_CLIENT_LABEL:-}" ]] && hdrs+=(-H "X-NM-Client-Label: ${NM_CLIENT_LABEL}") [[ -n "${NM_REQUESTED_NAME:-}" ]] && hdrs+=(-H "X-NM-Requested-Name: ${NM_REQUESTED_NAME}") local code code="$(curl -sS -L --dump-header "${tmp_hdr}" -w '%{http_code}' -o "${tmp_conf}" "${hdrs[@]}" "${ep}")" if [[ "${code}" != "200" ]]; then echo "ERROR: client_conf HTTP ${code}" >&2 curl -sS -L "${hdrs[@]}" "${ep}" | head -c 400 >&2 || true exit 1 fi grep -q "^\[Interface\]" "${tmp_conf}" || { echo "ERROR: not a WireGuard conf"; head -n 20 "${tmp_conf}"; exit 1; } # --- Extract Client-ID (one-liner, trim spaces/quotes) --- local client_id client_id="$(grep -i '^Client-ID:' "${tmp_hdr}" | head -n1 | cut -d: -f2- | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/^"//; s/"$//' -e "s/^'//; s/'$//")" if [[ -z "${client_id}" ]]; then echo "ERROR: Client-ID header missing in response; cannot manage lifecycle." >&2 exit 1 fi echo "[*] Client-ID: ${client_id}" # Optional marker if ! grep -q "^#interface-name=" "${tmp_conf}"; then echo "#interface-name=${WG_IFACE}" | cat - "${tmp_conf}" > "${tmp_conf}.tmp" && mv "${tmp_conf}.tmp" "${tmp_conf}" fi # Install & bring up sudo mkdir -p "${WG_CONF_DIR}" sudo mv "${tmp_conf}" "${WG_CONF_DIR}/${WG_IFACE}.conf" sudo chmod 600 "${WG_CONF_DIR}/${WG_IFACE}.conf" echo "[*] Bringing up ${WG_IFACE} ..." sudo wg-quick up "${WG_IFACE}" echo "==== ${WG_IFACE} is up ====" ip addr show "${WG_IFACE}" || true wg show "${WG_IFACE}" || true # Persist state cat > "${STATE_FILE}" </dev/null || sudo rm -f "${WG_CONF_DIR}/${WG_IFACE}.conf" fi # Delete ephemeral client on server (if we know its ID) if [[ -n "${client_id}" ]]; then local del_ep="${NETMAKER_BASE_URL}/api/extclients/${NETMAKER_NETWORK}/${client_id}" echo "[*] Deleting client: DELETE ${del_ep}" local http http="$(curl -sS -o /dev/null -w '%{http_code}' -X DELETE -H "Authorization: Bearer ${NETMAKER_API_JWT}" "${del_ep}")" if [[ "${http}" =~ ^20[0-9]$ ]]; then echo "[*] Client deleted (HTTP ${http})." else echo "WARN: deletion returned HTTP ${http}; verify server state." fi else echo "WARN: client id not known (missing --client-id and state file); skipping server delete." fi rm -f "${STATE_FILE}" || true echo "[*] Teardown finished." } case "${SUBCMD}" in up) do_up ;; down) do_down ;; *) usage; exit 2 ;; esac