From 0116e4b9059cf48cea25acc67e6ce9341d7e264c Mon Sep 17 00:00:00 2001 From: Thomas Stallinger Date: Mon, 13 Jul 2026 09:25:10 +0200 Subject: [PATCH] feat: implement provisioning server handshake --- scripts/modules/15_server_handshake.sh | 139 +++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 scripts/modules/15_server_handshake.sh diff --git a/scripts/modules/15_server_handshake.sh b/scripts/modules/15_server_handshake.sh new file mode 100755 index 0000000..b87f739 --- /dev/null +++ b/scripts/modules/15_server_handshake.sh @@ -0,0 +1,139 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +readonly SCRIPT_NAME="${0##*/}" + +readonly NETWORK_STATE="/run/tuxflotte/network/state.env" +readonly HARDWARE_FILE="/run/tuxflotte/hardware/hardware.json" + +readonly RUNTIME_DIR="/run/tuxflotte/server" +readonly ACTIVATION_FILE="${RUNTIME_DIR}/activation.json" + +readonly RESPONSE_FILE="${RUNTIME_DIR}/response.json" + +log() { + printf '[%s] %s\n' "${SCRIPT_NAME}" "$*" >&2 +} + +fatal() { + printf '[%s] FEHLER: %s\n' "${SCRIPT_NAME}" "$*" >&2 + exit 1 +} + +require_root() { + if [[ "${EUID}" -ne 0 ]]; then + fatal "Das Server-Handshake-Modul muss als root ausgeführt werden." + fi +} + +prepare_runtime_directory() { + install -d \ + --mode=0700 \ + --owner=root \ + --group=root \ + "${RUNTIME_DIR}" + + rm -f -- "${ACTIVATION_FILE}" "${RESPONSE_FILE}" +} + +validate_inputs() { + [[ -r "${NETWORK_STATE}" ]] || + fatal "Netzwerkstatus nicht gefunden: ${NETWORK_STATE}" + + [[ -r "${HARDWARE_FILE}" ]] || + fatal "Hardwareinformationen nicht gefunden: ${HARDWARE_FILE}" + + jq --exit-status . "${HARDWARE_FILE}" >/dev/null || + fatal "Hardwaredatei enthält kein gültiges JSON." +} + +build_activation_request() { + local activation_code="$1" + local hostname + local machine_id + + hostname="$(hostname)" + machine_id="$( + jq --raw-output \ + '.identity.machine_id // empty' \ + "${HARDWARE_FILE}" + )" + + jq \ + --null-input \ + --arg activation_code "${activation_code}" \ + --arg hostname "${hostname}" \ + --arg machine_id "${machine_id}" \ + --arg client_version "0.1.0" \ + --slurpfile hardware "${HARDWARE_FILE}" \ + '{ + activation_code: $activation_code, + device_fingerprint: $hardware[0].identity.device_fingerprint, + hostname: $hostname, + machine_id: ( + if $machine_id == "" + then null + else $machine_id + end + ), + client_version: $client_version, + hardware: $hardware[0] + }' +} + +send_activation_request() { + local server_url + + # shellcheck disable=SC1090 + source "${NETWORK_STATE}" + + server_url="${TUXFLOTTE_SERVER_URL%/health}" + + curl \ + --silent \ + --show-error \ + --fail \ + --location \ + --header 'Content-Type: application/json' \ + --data-binary "@${ACTIVATION_FILE}" \ + --output "${RESPONSE_FILE}" \ + "${server_url}/api/v1/activate" || + fatal "Provisioning-Server konnte nicht erfolgreich kontaktiert werden." + + chmod 0600 "${RESPONSE_FILE}" + + jq --exit-status . "${RESPONSE_FILE}" >/dev/null || + fatal "Serverantwort enthält kein gültiges JSON." + + jq --exit-status '.success == true' "${RESPONSE_FILE}" >/dev/null || + fatal "Provisioning-Server hat die Aktivierung abgelehnt." + + log "Provisioning-Handshake erfolgreich abgeschlossen." +} + +main() { + require_root + prepare_runtime_directory + validate_inputs + + log "Eingabedaten für den Provisioning-Handshake sind gültig." + + local activation_code + + activation_code="${TUXFLOTTE_ACTIVATION_CODE:-LAB-2026-START}" + + build_activation_request "${activation_code}" >"${ACTIVATION_FILE}" + + chmod 0600 "${ACTIVATION_FILE}" + + jq --exit-status . "${ACTIVATION_FILE}" >/dev/null || + fatal "Aktivierungsrequest enthält kein gültiges JSON." + + log "Aktivierungsrequest wurde unter ${ACTIVATION_FILE} gespeichert." + + send_activation_request + +} + +main "$@"