#!/bin/sh set -eu MANAGER_URL="http://vulnfixer.codata.pb.gov.br" ENROLLMENT_TOKEN="" INSTALL_ROOT="/opt/vulnfixer" DATA_ROOT="/var/lib/vulnfixer" ENV_FILE="/etc/default/vulnfixer-agent" SERVICE_NAME="vulnfixer-agent" POLL_INTERVAL_SECONDS="900" INVENTORY_INTERVAL_SECONDS="7200" ALLOW_COMMANDS="apt,apt-get,sh,bash,cmd,powershell,winget,brew,softwareupdate,yum,dnf,zypper,snap" INSECURE_TLS="false" RESET_STATE="false" usage() { cat <<'EOF' Uso: ./install.sh --token TOKEN [opcoes] Fluxo mais simples: wget http://vulnfixer.codata.pb.gov.br/install.sh -O install.sh chmod +x ./install.sh sudo ./install.sh --token TOKEN Opcoes: --token TOKEN --manager-url URL --install-root CAMINHO --data-root CAMINHO --service-name NOME --poll-interval SEGUNDOS --inventory-interval SEGUNDOS --allow-commands LISTA --insecure-tls true|false --reset-state true|false -h, --help EOF } normalize_manager_url() { value="$1" value="${value%"${value##*[![:space:]]}"}" value="${value#"${value%%[![:space:]]*}"}" if [ -z "$value" ]; then echo "" return fi if [ "${value#*://}" = "$value" ]; then value="https://$value" fi case "$value" in http://localhost*|http://127.0.0.1*|http://[::1]*) printf '%s ' "${value%/}" return ;; esac case "$value" in http://*) value="https://${value#http://}" ;; esac case "$value" in http://*|https://*) ;; *) echo "URL do manager invalida: use http(s)://host" >&2 exit 1 ;; esac printf '%s ' "${value%/}" } download_file() { url="$1" destination="$2" if command -v curl >/dev/null 2>&1; then if [ "$INSECURE_TLS" = "true" ]; then curl -k -fsSL -H "X-Enrollment-Token: ${ENROLLMENT_TOKEN}" -o "$destination" "$url" else curl -fsSL -H "X-Enrollment-Token: ${ENROLLMENT_TOKEN}" -o "$destination" "$url" fi return fi if command -v wget >/dev/null 2>&1; then if [ "$INSECURE_TLS" = "true" ]; then wget --no-check-certificate --header="X-Enrollment-Token: ${ENROLLMENT_TOKEN}" -O "$destination" "$url" else wget --header="X-Enrollment-Token: ${ENROLLMENT_TOKEN}" -O "$destination" "$url" fi return fi echo "Instale curl ou wget para continuar." >&2 exit 1 } while [ "$#" -gt 0 ]; do case "$1" in --token|--enrollment-token) ENROLLMENT_TOKEN="${2:-}" shift 2 ;; --manager-url) MANAGER_URL="${2:-}" shift 2 ;; --install-root) INSTALL_ROOT="${2:-}" shift 2 ;; --data-root) DATA_ROOT="${2:-}" shift 2 ;; --service-name) SERVICE_NAME="${2:-}" shift 2 ;; --poll-interval) POLL_INTERVAL_SECONDS="${2:-}" shift 2 ;; --inventory-interval) INVENTORY_INTERVAL_SECONDS="${2:-}" shift 2 ;; --allow-commands) ALLOW_COMMANDS="${2:-}" shift 2 ;; --insecure-tls) INSECURE_TLS="${2:-false}" shift 2 ;; --reset-state) RESET_STATE="${2:-false}" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Opcao desconhecida: $1" >&2 usage exit 1 ;; esac done if [ -z "$ENROLLMENT_TOKEN" ]; then echo "--token e obrigatorio." >&2 usage exit 1 fi MANAGER_URL="$(normalize_manager_url "$MANAGER_URL")" if [ "$(id -u)" -ne 0 ]; then if command -v sudo >/dev/null 2>&1; then exec sudo sh "$0" "$@" fi echo "Execute como root ou use sudo." >&2 exit 1 fi if ! command -v systemctl >/dev/null 2>&1; then echo "systemctl nao encontrado. Este instalador requer systemd." >&2 exit 1 fi ARCH="$(uname -m)" case "$ARCH" in x86_64|amd64) ARTIFACT_ARCH="amd64" ;; aarch64|arm64) ARTIFACT_ARCH="arm64" ;; *) echo "Arquitetura Linux ainda nao suportada por este instalador rapido: $ARCH" >&2 exit 1 ;; esac TMP_DIR="$(mktemp -d)" cleanup() { rm -rf "$TMP_DIR" } trap 'cleanup' EXIT json_get() { file_path="$1" key="$2" if command -v python3 >/dev/null 2>&1; then python3 - "$file_path" "$key" <<'PY' import json import sys path = sys.argv[1] key = sys.argv[2] with open(path, "r", encoding="utf-8") as handle: data = json.load(handle) value = data.get(key) if value is None: sys.exit(1) print(value) PY return $? fi value="$(sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$file_path" | head -n 1)" if [ -z "${value:-}" ]; then return 1 fi printf '%s ' "$value" } sha256_file() { file_path="$1" if command -v sha256sum >/dev/null 2>&1; then sha256sum "$file_path" | awk '{print $1}' return 0 fi if command -v shasum >/dev/null 2>&1; then shasum -a 256 "$file_path" | awk '{print $1}' return 0 fi return 1 } LATEST_META_URL="$MANAGER_URL/api/v1/agent-updates/latest?platform=linux&architecture=$ARTIFACT_ARCH" if ! download_file "$LATEST_META_URL" "$TMP_DIR/latest.json"; then if [ "$ARTIFACT_ARCH" != "amd64" ]; then ARTIFACT_ARCH="amd64" LATEST_META_URL="$MANAGER_URL/api/v1/agent-updates/latest?platform=linux&architecture=$ARTIFACT_ARCH" download_file "$LATEST_META_URL" "$TMP_DIR/latest.json" else echo "Nao foi possivel consultar metadata de atualizacao do agente no manager." >&2 exit 1 fi fi BINARY_PATH="$TMP_DIR/vulnfixer-agent" DOWNLOAD_URL="$(json_get "$TMP_DIR/latest.json" "download_url" || true)" EXPECTED_SHA256="$(json_get "$TMP_DIR/latest.json" "sha256" || true)" if [ -z "${DOWNLOAD_URL:-}" ]; then DOWNLOAD_URL="$MANAGER_URL/api/v1/agent-updates/download/binary-linux-$ARTIFACT_ARCH" elif [ "${DOWNLOAD_URL#/}" != "$DOWNLOAD_URL" ]; then DOWNLOAD_URL="$MANAGER_URL$DOWNLOAD_URL" fi download_file "$DOWNLOAD_URL" "$BINARY_PATH" if [ -n "${EXPECTED_SHA256:-}" ]; then ACTUAL_SHA256="$(sha256_file "$BINARY_PATH" || true)" if [ -n "${ACTUAL_SHA256:-}" ]; then expected_lc="$(printf '%s' "$EXPECTED_SHA256" | tr '[:upper:]' '[:lower:]')" actual_lc="$(printf '%s' "$ACTUAL_SHA256" | tr '[:upper:]' '[:lower:]')" if [ "$actual_lc" != "$expected_lc" ]; then echo "SHA256 do binario baixado nao confere. Esperado: $EXPECTED_SHA256 Atual: $ACTUAL_SHA256" >&2 exit 1 fi fi fi install -d -m 0755 "$INSTALL_ROOT" install -d -m 0755 "$DATA_ROOT" if systemctl list-unit-files | grep -q "^${SERVICE_NAME}\.service"; then systemctl stop "$SERVICE_NAME" >/dev/null 2>&1 || true fi install -m 0755 "$BINARY_PATH" "$INSTALL_ROOT/vulnfixer-agent" STATE_PATH="$DATA_ROOT/agent-state.json" if [ "$RESET_STATE" = "true" ]; then rm -f "$STATE_PATH" fi if [ ! -f "$STATE_PATH" ]; then printf '%s ' '{"agent_id":"","agent_secret":""}' > "$STATE_PATH" chmod 0600 "$STATE_PATH" fi cat > "$ENV_FILE" < "/etc/systemd/system/${SERVICE_NAME}.service" <