Add PatchMon enrollment helper

This commit is contained in:
2026-09-18 01:03:09 +00:00
parent a107caac99
commit 03e36a3a7e
2 changed files with 224 additions and 0 deletions
+35
View File
@@ -61,6 +61,41 @@ chmod +x install-docker.sh
The old `Docker-Install.sh` filename still exists as a compatibility wrapper.
## PatchMon enrollment
Enroll a Linux host into PatchMon. By default this uses the PatchMon LXC at `http://192.168.20.232:3000` and the `direct-host` enrollment type.
Recommended interactive one-liner. It prompts for the PatchMon auto-enrollment token key and secret:
```bash
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo bash
```
Non-interactive direct-host enrollment:
```bash
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo bash -s -- --token-key '<key>' --token-secret '<secret>' --type direct-host -y
```
Proxmox LXC enrollment:
```bash
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo bash -s -- --token-key '<key>' --token-secret '<secret>' --type proxmox-lxc -y
```
Useful options:
```text
--server URL PatchMon base URL. Default: http://192.168.20.232:3000
--type TYPE direct-host or proxmox-lxc. Default: direct-host
--friendly-name NAME Friendly name to report to PatchMon
--force Ask PatchMon's generated installer to force/reinstall when supported
--print-script Download and print the generated PatchMon script instead of running it
-y, --yes Non-interactive mode
```
Token note: keep the token secret out of Git. Store it in a password manager or a local `.env` and paste/use it at enrollment time only.
## Options
```text
+189
View File
@@ -0,0 +1,189 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_NAME="$(basename "$0")"
PATCHMON_SERVER="${PATCHMON_SERVER:-http://192.168.20.232:3000}"
ENROLL_TYPE="${PATCHMON_ENROLL_TYPE:-direct-host}"
TOKEN_KEY="${PATCHMON_AUTO_ENROLL_KEY:-}"
TOKEN_SECRET="${PATCHMON_AUTO_ENROLL_SECRET:-}"
FRIENDLY_NAME_VALUE="${FRIENDLY_NAME:-}"
FORCE_INSTALL="false"
PRINT_ONLY="false"
YES="false"
usage() {
cat <<'USAGE'
Enroll this Linux host into PatchMon using PatchMon's auto-enrollment script.
Usage:
./patchmon-enroll.sh [options]
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo bash -s -- [options]
Options:
--server URL PatchMon base URL. Default: http://192.168.20.232:3000
--type TYPE Enrollment type: direct-host or proxmox-lxc. Default: direct-host
--token-key KEY PatchMon auto-enrollment token key. Can also use PATCHMON_AUTO_ENROLL_KEY.
--token-secret SECRET PatchMon auto-enrollment token secret. Can also use PATCHMON_AUTO_ENROLL_SECRET.
--friendly-name NAME Friendly name to report to PatchMon.
--force Ask PatchMon's generated installer to force/reinstall when supported.
--print-script Download and print the generated PatchMon script instead of running it.
-y, --yes Non-interactive. Fail if token values are missing instead of prompting.
-h, --help Show this help.
Recommended one-liner, prompts for token if env vars are not set:
curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo bash
Non-interactive one-liner:
PATCHMON_AUTO_ENROLL_KEY='<key>' PATCHMON_AUTO_ENROLL_SECRET='<secret>' curl -fsSL https://gitea.wheelytho.com/Wheelz/Docker-Install/raw/branch/main/patchmon-enroll.sh | sudo -E bash -s -- --type direct-host -y
USAGE
}
log() { printf '\n[PatchMon-Enroll] %s\n' "$*"; }
warn() { printf '\n[PatchMon-Enroll WARNING] %s\n' "$*" >&2; }
fail() { printf '\n[PatchMon-Enroll ERROR] %s\n' "$*" >&2; exit 1; }
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
}
urlencode() {
# POSIX-ish URL encoding via Python, which is present on the supported Ubuntu/Debian hosts.
python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$1"
}
prompt_secret() {
local prompt="$1"
local value=""
if [[ "$YES" == "true" ]]; then
fail "$prompt is required. Provide it with --token-key/--token-secret or PATCHMON_AUTO_ENROLL_KEY/PATCHMON_AUTO_ENROLL_SECRET."
fi
if [[ -t 0 ]]; then
read -r -s -p "$prompt: " value
printf '\n' >&2
else
if [[ -r /dev/tty ]]; then
read -r -s -p "$prompt: " value </dev/tty
printf '\n' >&2
else
fail "$prompt is required, and no TTY is available for prompting."
fi
fi
printf '%s' "$value"
}
while [[ $# -gt 0 ]]; do
case "$1" in
--server)
[[ $# -ge 2 ]] || fail "--server requires a URL"
PATCHMON_SERVER="$2"
shift 2
;;
--type)
[[ $# -ge 2 ]] || fail "--type requires direct-host or proxmox-lxc"
ENROLL_TYPE="$2"
shift 2
;;
--token-key)
[[ $# -ge 2 ]] || fail "--token-key requires a value"
TOKEN_KEY="$2"
shift 2
;;
--token-secret)
[[ $# -ge 2 ]] || fail "--token-secret requires a value"
TOKEN_SECRET="$2"
shift 2
;;
--friendly-name)
[[ $# -ge 2 ]] || fail "--friendly-name requires a value"
FRIENDLY_NAME_VALUE="$2"
shift 2
;;
--force)
FORCE_INSTALL="true"
shift
;;
--print-script)
PRINT_ONLY="true"
shift
;;
-y|--yes)
YES="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
fail "Unknown option: $1"
;;
esac
done
case "$ENROLL_TYPE" in
direct-host|proxmox-lxc) ;;
*) fail "--type must be direct-host or proxmox-lxc. Got: $ENROLL_TYPE" ;;
esac
PATCHMON_SERVER="${PATCHMON_SERVER%/}"
[[ "$PATCHMON_SERVER" =~ ^https?:// ]] || fail "--server must start with http:// or https://"
need_cmd curl
need_cmd python3
need_cmd mktemp
if [[ -z "$TOKEN_KEY" ]]; then
TOKEN_KEY="$(prompt_secret 'PatchMon auto-enrollment token key')"
fi
if [[ -z "$TOKEN_SECRET" ]]; then
TOKEN_SECRET="$(prompt_secret 'PatchMon auto-enrollment token secret')"
fi
[[ -n "$TOKEN_KEY" ]] || fail "Token key cannot be empty"
[[ -n "$TOKEN_SECRET" ]] || fail "Token secret cannot be empty"
if [[ "$PRINT_ONLY" != "true" && "$(id -u)" -ne 0 ]]; then
fail "This script must be run as root because PatchMon's generated installer installs a system agent. Use sudo."
fi
query="type=$(urlencode "$ENROLL_TYPE")&force=$(urlencode "$FORCE_INSTALL")&token_key=$(urlencode "$TOKEN_KEY")&token_secret=$(urlencode "$TOKEN_SECRET")"
script_url="${PATCHMON_SERVER}/api/v1/auto-enrollment/script?${query}"
tmp_script="$(mktemp /tmp/patchmon-enroll.XXXXXX.sh)"
cleanup() { rm -f "$tmp_script"; }
trap cleanup EXIT
log "Downloading PatchMon generated enrollment script"
if ! curl -fsSL "$script_url" -o "$tmp_script"; then
fail "Failed to download PatchMon enrollment script. Check server URL, token, allowed IP range, and network access."
fi
if ! head -n 1 "$tmp_script" | grep -q '^#!'; then
warn "PatchMon did not return a shell script. First lines follow:"
sed -n '1,10p' "$tmp_script" >&2
fail "Unexpected response from PatchMon enrollment endpoint"
fi
chmod 700 "$tmp_script"
if [[ "$PRINT_ONLY" == "true" ]]; then
sed -e "s/${TOKEN_SECRET//\//\\/}/<redacted>/g" -e "s/${TOKEN_KEY//\//\\/}/<redacted>/g" "$tmp_script"
exit 0
fi
log "Running PatchMon enrollment script as type: $ENROLL_TYPE"
if [[ -n "$FRIENDLY_NAME_VALUE" ]]; then
FRIENDLY_NAME="$FRIENDLY_NAME_VALUE" sh "$tmp_script"
else
sh "$tmp_script"
fi
log "Verifying local PatchMon agent service"
if command -v systemctl >/dev/null 2>&1; then
if systemctl list-unit-files 'patchmon*' --no-legend 2>/dev/null | grep -q .; then
systemctl --no-pager --full status 'patchmon*' || true
else
warn "No patchmon systemd unit was found. Check the installer output above."
fi
else
warn "systemctl not available; skipping service verification."
fi
log "Enrollment wrapper completed"