Files
Varaverk/Plugin/unraid/api/create_api_key.php
T
Gmer4Lfe c34224effa Carry the CSRF token on fetch requests and put mutations behind POST
Unraid already enforces CSRF on every POST via auto_prepend, but its
injector is jQuery-only — the plugin's native fetch() calls carried no
token and were being terminated before the endpoint ran, silently,
because csrf_terminate exits with an empty body that r.json() swallows.
2026-08-02 10:28:53 -04:00

85 lines
4.7 KiB
PHP

<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
// Unraid API key provisioning. Runs unraid_api_key_renew.sh for this host and reports a
// masked preview of the key that ended up in the conf — the "create key" action on the
// setup and partnership tabs.
//
// OPERATIONAL MODEL
// Provisioning for the local host only. There is no host parameter: the target is whatever
// vv_detect_host() resolves to, because a key can only be created on the machine that owns
// the API it authenticates against. A partner's key is created on the partner.
//
// Repair, not just creation. The underlying script is the same one the array-start and
// 15-minute watchdog runs call, because the registry it writes can be cleared by a service
// restart rather than only by a reboot. Running it against a host that already has a valid
// key is a no-op that re-registers, which is why this endpoint is safe to press twice.
//
// DESIGN PRINCIPLES
// Idempotent by delegation.
// This file contains no key logic at all. Whether a key needs creating, renewing, or
// leaving alone is decided in one place — the shell script — so the UI path and the
// scheduled path can never diverge on that judgement.
//
// The key is never returned.
// Only a masked preview (first 8, last 4) leaves the server. The full value lives in
// host*.conf, which is the only place anything reads it from. There is no workflow that
// needs the key in a browser, so it is not sent to one.
//
// OPERATIONAL SAFEGUARDS
// The host slot is validated before it is used to compose a conf filename.
// vv_detect_host() can return 'unknown' when hostname matching fails, and 'unknown.conf'
// is not a file that should be read or written. ^host\d+$ is enforced first, so a host
// this plugin cannot identify gets a clear error instead of a confusing failure deeper
// in the script.
//
// A missing script is reported, not executed.
// vv_auto_create_api_key() checks file_exists() before exec(), so a partial deploy
// returns a named error rather than a shell failure surfacing as an empty key.
//
// The script run is externally time-boxed.
// `timeout 120` wraps it inside the library — PHP's own limit does not cover exec()
// time on Linux, so a stalled unraid-api call would otherwise hold a php-fpm worker
// open indefinitely. Exit 124 is reported as a timeout, distinctly from a script error.
//
// Never cached.
// Cache-Control: no-store, no-cache. A cached provisioning response would report an old
// key preview after a genuine renewal — the one moment the preview matters.
//
// POST only, which is what places it behind Unraid's CSRF guard.
// The platform prepend validates the token on every POST and inspects no GET at all, so
// a state-changing action reachable by GET is a state-changing action with no CSRF
// protection. This was previously a GET; the callers now POST and the token travels as
// an X-CSRF-Token header set by the shim in Varaverk.page. See README-unraid.md.
//
// REQUEST
// POST, no parameters (the target host is the local host, by construction)
//
// RESPONSE
// {"ok":true,"key_preview":"abcd1234...wxyz"} or "registered" when the key is not readable
// {"ok":false,"error":string} unknown host, missing script, or timeout
//
// DEPENDS ON
// include/config.php vv_detect_host(), vv_auto_create_api_key()
// System_Essentials/unraid_api_key_renew.sh
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
header('Cache-Control: no-store, no-cache');
require_once dirname(__DIR__) . '/include/config.php';
// POST is what puts this behind Unraid's CSRF guard — the platform prepend validates every
// POST and ignores every GET, so a state-changing action must not be reachable by GET.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['ok' => false, 'error' => 'POST only']);
exit;
}
$host = vv_detect_host();
if (!preg_match('/^host\d+$/', $host)) {
echo json_encode(['ok' => false, 'error' => 'Cannot detect local host']);
exit;
}
echo json_encode(vv_auto_create_api_key($host, $host . '.conf'));