mirror of
https://github.com/harivansh-afk/betterNAS.git
synced 2026-04-15 06:04:40 +00:00
Secure first-loop control-plane auth and mount routing.
Protect the control-plane API with explicit bearer auth, add node-scoped registration/heartbeat credentials, and make export mount paths an explicit contract field so mount profiles stay correct across runtimes. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
a7f85f4871
commit
ed40da7326
23 changed files with 3676 additions and 124 deletions
|
|
@ -11,22 +11,55 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../lib/runtime-env.sh"
|
|||
control_health="$(curl -fsS "http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/health")"
|
||||
echo "$control_health" | jq -e '.service == "control-plane" and .status == "ok"' >/dev/null
|
||||
|
||||
register_response="$(
|
||||
curl -fsS \
|
||||
-X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d @- \
|
||||
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/nodes/register" <<JSON
|
||||
{"machineId":"${BETTERNAS_CLONE_NAME}-machine","displayName":"${BETTERNAS_CLONE_NAME} node","agentVersion":"${BETTERNAS_VERSION}","directAddress":"${BETTERNAS_NODE_DIRECT_ADDRESS}","relayAddress":null,"exports":[{"label":"integration","path":"${BETTERNAS_EXPORT_PATH}","protocols":["webdav"],"capacityBytes":null,"tags":["integration"]}]}
|
||||
register_headers="$(mktemp)"
|
||||
register_body="$(mktemp)"
|
||||
trap 'rm -f "$register_headers" "$register_body"' EXIT
|
||||
|
||||
curl -fsS \
|
||||
-D "$register_headers" \
|
||||
-o "$register_body" \
|
||||
-X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H "Authorization: Bearer ${BETTERNAS_CONTROL_PLANE_NODE_BOOTSTRAP_TOKEN}" \
|
||||
-d @- \
|
||||
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/nodes/register" <<JSON
|
||||
{"machineId":"${BETTERNAS_CLONE_NAME}-machine","displayName":"${BETTERNAS_CLONE_NAME} node","agentVersion":"${BETTERNAS_VERSION}","directAddress":"${BETTERNAS_NODE_DIRECT_ADDRESS}","relayAddress":null,"exports":[{"label":"integration","path":"${BETTERNAS_EXPORT_PATH}","mountPath":"/dav/","protocols":["webdav"],"capacityBytes":null,"tags":["integration"]}]}
|
||||
JSON
|
||||
)"
|
||||
|
||||
register_response="$(cat "$register_body")"
|
||||
echo "$register_response" | jq -e '.status == "online"' >/dev/null
|
||||
node_id="$(echo "$register_response" | jq -er '.id')"
|
||||
node_token="$(tr -d '\r' < "$register_headers" | awk -F': ' 'tolower($1) == tolower("X-BetterNAS-Node-Token") { print $2 }' | tail -n 1 | tr -d '\n')"
|
||||
if [[ -z "$node_token" ]]; then
|
||||
echo "Node registration did not return X-BetterNAS-Node-Token" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
heartbeat_status="$(curl -sS -o /dev/null -w '%{http_code}' \
|
||||
-X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H "Authorization: Bearer ${node_token}" \
|
||||
-d "{\"nodeId\":\"${node_id}\",\"status\":\"online\",\"lastSeenAt\":\"2026-01-01T00:00:00Z\"}" \
|
||||
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/nodes/${node_id}/heartbeat")"
|
||||
if [[ "$heartbeat_status" != "204" ]]; then
|
||||
echo "Heartbeat did not return 204" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exports_response="$(curl -fsS -H "Authorization: Bearer ${BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN}" "http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/exports")"
|
||||
export_id="$(
|
||||
echo "$exports_response" | jq -er \
|
||||
--arg node_id "$node_id" \
|
||||
--arg export_path "$BETTERNAS_EXPORT_PATH" \
|
||||
'map(select(.nasNodeId == $node_id and .path == $export_path)) | .[0].id'
|
||||
)"
|
||||
|
||||
mount_profile="$(
|
||||
curl -fsS \
|
||||
-X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"userId":"integration-user","deviceId":"integration-device","exportId":"dev-export"}' \
|
||||
-H "Authorization: Bearer ${BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN}" \
|
||||
-d "{\"userId\":\"integration-user\",\"deviceId\":\"integration-device\",\"exportId\":\"${export_id}\"}" \
|
||||
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/mount-profiles/issue"
|
||||
)"
|
||||
echo "$mount_profile" | jq -e --arg expected "$BETTERNAS_EXAMPLE_MOUNT_URL" '.protocol == "webdav" and .mountUrl == $expected' >/dev/null
|
||||
|
|
@ -35,10 +68,12 @@ cloud_profile="$(
|
|||
curl -fsS \
|
||||
-X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"userId":"integration-user","exportId":"dev-export","provider":"nextcloud"}' \
|
||||
-H "Authorization: Bearer ${BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN}" \
|
||||
-d "{\"userId\":\"integration-user\",\"exportId\":\"${export_id}\",\"provider\":\"nextcloud\"}" \
|
||||
"http://localhost:${BETTERNAS_CONTROL_PLANE_PORT}/api/v1/cloud-profiles/issue"
|
||||
)"
|
||||
echo "$cloud_profile" | jq -e --arg expected "$NEXTCLOUD_BASE_URL" '.provider == "nextcloud" and .baseUrl == $expected' >/dev/null
|
||||
echo "$cloud_profile" | jq -e --arg expected "/apps/betternascontrolplane/exports/${export_id}" '.path == $expected' >/dev/null
|
||||
|
||||
nextcloud_status="$(curl -fsS "${NEXTCLOUD_BASE_URL}/status.php")"
|
||||
echo "$nextcloud_status" | jq -e '.installed == true' >/dev/null
|
||||
|
|
@ -47,8 +82,8 @@ nextcloud_app_status="$(
|
|||
curl -fsS \
|
||||
-u "${NEXTCLOUD_ADMIN_USER}:${NEXTCLOUD_ADMIN_PASSWORD}" \
|
||||
-H 'OCS-APIRequest: true' \
|
||||
"${NEXTCLOUD_BASE_URL}/ocs/v2.php/apps/betternascontrolplane/api/status"
|
||||
"${NEXTCLOUD_BASE_URL}/ocs/v2.php/apps/betternascontrolplane/api/status?format=json"
|
||||
)"
|
||||
echo "$nextcloud_app_status" | jq -e '.ocs.meta.statuscode == 100' >/dev/null
|
||||
echo "$nextcloud_app_status" | jq -e '.ocs.meta.statuscode == 200' >/dev/null
|
||||
|
||||
echo "Stack verified for ${BETTERNAS_CLONE_NAME}."
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ betternas_write_agent_env_file() {
|
|||
betternas_write_env_assignment "BETTERNAS_NEXTCLOUD_PORT" "$nextcloud_port"
|
||||
betternas_write_env_assignment "BETTERNAS_EXPORT_PATH" ".state/${clone_name}/export"
|
||||
betternas_write_env_assignment "BETTERNAS_VERSION" "local-dev"
|
||||
betternas_write_env_assignment "BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN" "${clone_name}-local-client-token"
|
||||
betternas_write_env_assignment "BETTERNAS_CONTROL_PLANE_NODE_BOOTSTRAP_TOKEN" "${clone_name}-local-node-bootstrap-token"
|
||||
betternas_write_env_assignment "BETTERNAS_NODE_DIRECT_ADDRESS" "http://localhost:${node_agent_port}"
|
||||
betternas_write_env_assignment "BETTERNAS_EXAMPLE_MOUNT_URL" "http://localhost:${node_agent_port}/dav/"
|
||||
betternas_write_env_assignment "NEXTCLOUD_BASE_URL" "http://localhost:${nextcloud_port}"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ read -r default_nextcloud_port default_node_agent_port default_control_plane_por
|
|||
: "${BETTERNAS_NODE_AGENT_PORT:=$default_node_agent_port}"
|
||||
: "${BETTERNAS_NEXTCLOUD_PORT:=$default_nextcloud_port}"
|
||||
: "${BETTERNAS_VERSION:=local-dev}"
|
||||
: "${BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN:=${BETTERNAS_CLONE_NAME}-local-client-token}"
|
||||
: "${BETTERNAS_CONTROL_PLANE_NODE_BOOTSTRAP_TOKEN:=${BETTERNAS_CLONE_NAME}-local-node-bootstrap-token}"
|
||||
: "${NEXTCLOUD_ADMIN_USER:=admin}"
|
||||
: "${NEXTCLOUD_ADMIN_PASSWORD:=admin}"
|
||||
|
||||
|
|
@ -56,6 +58,8 @@ export BETTERNAS_NODE_AGENT_PORT
|
|||
export BETTERNAS_NEXTCLOUD_PORT
|
||||
export BETTERNAS_EXPORT_PATH
|
||||
export BETTERNAS_VERSION
|
||||
export BETTERNAS_CONTROL_PLANE_CLIENT_TOKEN
|
||||
export BETTERNAS_CONTROL_PLANE_NODE_BOOTSTRAP_TOKEN
|
||||
export NEXTCLOUD_ADMIN_USER
|
||||
export NEXTCLOUD_ADMIN_PASSWORD
|
||||
export BETTERNAS_NODE_DIRECT_ADDRESS
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue