清理无用代码
This commit is contained in:
1 parent
b1fedb4131
commit
7b1ad8584d
51 files changed
+3477
-1173
No files matched your search
+72
-27
@@ -21,6 +21,32 @@ step() {
|
||||
printf 'smoke: %s\n' "$1" >&2
|
||||
}
|
||||
|
||||
admin_token=
|
||||
server_id=
|
||||
latency_task_id=
|
||||
alert_rule_id=
|
||||
cleanup() {
|
||||
if [ "${MONITOR_KEEP_RESOURCES:-0}" != "1" ] && [ -n "$admin_token" ]; then
|
||||
if [ -n "$alert_rule_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/alert-rules/$alert_rule_id" >/dev/null || true
|
||||
fi
|
||||
if [ -n "$latency_task_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/latency-tasks/$latency_task_id" >/dev/null || true
|
||||
fi
|
||||
if [ -n "$server_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/servers/$server_id" >/dev/null || true
|
||||
fi
|
||||
fi
|
||||
if [ -n "$admin_token" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X POST "$MONITOR_BASE_URL/api/admin/logout" >/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
step "bootstrap"
|
||||
bootstrap_json=$(request "$MONITOR_BASE_URL/api/bootstrap")
|
||||
printf '%s' "$bootstrap_json" | jq -e '.access == "ok" and (.servers | type == "array")' >/dev/null
|
||||
@@ -33,12 +59,27 @@ const key = await crypto.subtle.importKey("raw", encoder.encode(process.env.NODE
|
||||
const bits = await crypto.subtle.deriveBits({ name: "PBKDF2", hash: "SHA-256", iterations: 600000, salt: encoder.encode(`nodeflare:${process.env.NODEFLARE_SALT}`) }, key, 256);
|
||||
console.log(Array.from(new Uint8Array(bits), byte => byte.toString(16).padStart(2, "0")).join(""));
|
||||
')
|
||||
step "login throttling"
|
||||
invalid_login_payload=$(jq -nc --arg username "$MONITOR_ADMIN_USERNAME" --arg password_derived "0000000000000000000000000000000000000000000000000000000000000000" '{username:$username,password_derived:$password_derived,turnstile_token:""}')
|
||||
attempt=1
|
||||
while [ "$attempt" -le 5 ]; do
|
||||
invalid_login_status=$(monitor_curl --silent --output /dev/null --write-out '%{http_code}' \
|
||||
-H 'Content-Type: application/json' -H 'X-Real-IP: 203.0.113.10' \
|
||||
--data "$invalid_login_payload" "$MONITOR_BASE_URL/api/admin/login")
|
||||
[ "$invalid_login_status" = "401" ]
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
throttled_login_status=$(monitor_curl --silent --output /dev/null --write-out '%{http_code}' \
|
||||
-H 'Content-Type: application/json' -H 'X-Real-IP: 203.0.113.10' \
|
||||
--data "$invalid_login_payload" "$MONITOR_BASE_URL/api/admin/login")
|
||||
[ "$throttled_login_status" = "429" ]
|
||||
step "static assets"
|
||||
security_headers=$(monitor_curl --silent --show-error --dump-header - --output /dev/null "$MONITOR_BASE_URL/")
|
||||
printf '%s' "$security_headers" | grep -qi '^x-content-type-options: nosniff'
|
||||
printf '%s' "$security_headers" | grep -qi '^x-frame-options: DENY'
|
||||
printf '%s' "$security_headers" | grep -qi "^content-security-policy:.*frame-ancestors 'none'"
|
||||
admin_html=$(request "$MONITOR_BASE_URL/admin")
|
||||
admin_html=$(request "$MONITOR_BASE_URL/admin/login")
|
||||
request "$MONITOR_BASE_URL/admin/servers" | grep -q '/admin-assets/'
|
||||
request "$MONITOR_BASE_URL/admin/about" | grep -q '/admin-assets/'
|
||||
admin_script=$(printf '%s' "$admin_html" | sed -n 's/.*src="\([^"]*\.js\)".*/\1/p' | head -n 1)
|
||||
admin_stylesheet=$(printf '%s' "$admin_html" | sed -n 's/.*href="\([^"]*\.css\)".*/\1/p' | head -n 1)
|
||||
@@ -50,11 +91,33 @@ admin_headers=$(monitor_curl --silent --show-error --dump-header - --output /dev
|
||||
printf '%s' "$admin_headers" | grep -qi '^cache-control:.*no-store'
|
||||
|
||||
step "login and settings"
|
||||
login_payload=$(jq -nc --arg username "$MONITOR_ADMIN_USERNAME" --arg password_derived "$password_derived" --arg turnstile_token "$MONITOR_TURNSTILE_TOKEN" '{username:$username,password_derived:$password_derived,turnstile_token:$turnstile_token}')
|
||||
login_json=$(request -H 'Content-Type: application/json' \
|
||||
--data "$(jq -nc --arg username "$MONITOR_ADMIN_USERNAME" --arg password "$MONITOR_ADMIN_PASSWORD" --arg password_derived "$password_derived" --arg turnstile_token "$MONITOR_TURNSTILE_TOKEN" '{username:$username,password:$password,password_derived:$password_derived,turnstile_token:$turnstile_token}')" \
|
||||
--data "$login_payload" \
|
||||
"$MONITOR_BASE_URL/api/admin/login")
|
||||
admin_token=$(printf '%s' "$login_json" | jq -er '.token')
|
||||
|
||||
step "login devices"
|
||||
second_login_json=$(request -H 'Content-Type: application/json' \
|
||||
--data "$login_payload" \
|
||||
"$MONITOR_BASE_URL/api/admin/login")
|
||||
second_admin_token=$(printf '%s' "$second_login_json" | jq -er '.token')
|
||||
sessions_json=$(request -H "Authorization: Bearer $admin_token" \
|
||||
"$MONITOR_BASE_URL/api/admin/sessions")
|
||||
printf '%s' "$sessions_json" | jq -e '
|
||||
(.sessions | length) >= 2 and
|
||||
([.sessions[] | select(.current == true)] | length) == 1 and
|
||||
([.sessions[] | select(.current == false)] | length) >= 1 and
|
||||
(.sessions | all((.ip_address | length) > 0 and (.user_agent | length) > 0))
|
||||
' >/dev/null
|
||||
second_session_id=$(request -H "Authorization: Bearer $second_admin_token" \
|
||||
"$MONITOR_BASE_URL/api/admin/sessions" | jq -er '.sessions[] | select(.current == true) | .id')
|
||||
request -H "Authorization: Bearer $admin_token" -X DELETE \
|
||||
"$MONITOR_BASE_URL/api/admin/sessions/$second_session_id" >/dev/null
|
||||
revoked_session_status=$(monitor_curl --silent --output /dev/null --write-out '%{http_code}' \
|
||||
-H "Authorization: Bearer $second_admin_token" "$MONITOR_BASE_URL/api/admin/settings")
|
||||
[ "$revoked_session_status" = "401" ]
|
||||
|
||||
# Login protection defaults to enabled, but without a complete Turnstile pair it
|
||||
# remains inactive so the first admin settings save must still work.
|
||||
settings_payload=$(request -H "Authorization: Bearer $admin_token" \
|
||||
@@ -74,28 +137,6 @@ request -H "Authorization: Bearer $admin_token" \
|
||||
|
||||
server_input='{"name":"Smoke Test Node","region":"JP","group_name":"Test","tags":"smoke","hidden":false,"expires_at":1893456000,"traffic_limit":107374182400,"traffic_limit_type":"max","price":9.9,"billing_cycle":30,"currency":"USD","auto_renewal":true,"network_interface":"","reset_day":1,"report_interval":60,"collect_interval":5,"rx_correction":0,"tx_correction":0,"agent_mirror":"https://mirror.example.com","offline_notify_disabled":false,"auto_update":true}'
|
||||
|
||||
server_id=
|
||||
latency_task_id=
|
||||
alert_rule_id=
|
||||
cleanup() {
|
||||
if [ "${MONITOR_KEEP_RESOURCES:-0}" = "1" ]; then
|
||||
return
|
||||
fi
|
||||
if [ -n "$alert_rule_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/alert-rules/$alert_rule_id" >/dev/null || true
|
||||
fi
|
||||
if [ -n "$latency_task_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/latency-tasks/$latency_task_id" >/dev/null || true
|
||||
fi
|
||||
if [ -n "$server_id" ]; then
|
||||
monitor_curl --silent --show-error -H "Authorization: Bearer $admin_token" \
|
||||
-X DELETE "$MONITOR_BASE_URL/api/admin/servers/$server_id" >/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
step "admin resources"
|
||||
request -H "Authorization: Bearer $admin_token" \
|
||||
"$MONITOR_BASE_URL/api/admin/telegram" | \
|
||||
@@ -123,11 +164,15 @@ MONITOR_BASE_URL="$MONITOR_BASE_URL" MONITOR_ADMIN_TOKEN="$admin_token" \
|
||||
MONITOR_LATENCY_TASK_ID="$latency_task_id" \
|
||||
node scripts/websocket-smoke.mjs
|
||||
token_without_admin_status=$(monitor_curl --silent --output /dev/null --write-out '%{http_code}' \
|
||||
-X POST \
|
||||
"$MONITOR_BASE_URL/api/admin/servers/$server_id/token")
|
||||
[ "$token_without_admin_status" = "401" ]
|
||||
request -H "Authorization: Bearer $admin_token" \
|
||||
"$MONITOR_BASE_URL/api/admin/servers/$server_id/token" | \
|
||||
jq -e --arg token "$agent_token" '.agent_token == $token' >/dev/null
|
||||
rotated_agent_token=$(MONITOR_BASE_URL="$MONITOR_BASE_URL" MONITOR_ADMIN_TOKEN="$admin_token" \
|
||||
MONITOR_AGENT_TOKEN="$agent_token" MONITOR_SERVER_ID="$server_id" \
|
||||
MONITOR_LATENCY_TASK_ID="$latency_task_id" MONITOR_CONFIG_ONLY=1 \
|
||||
MONITOR_ROTATE_AGENT_TOKEN=1 node scripts/websocket-smoke.mjs)
|
||||
[ "$rotated_agent_token" != "$agent_token" ]
|
||||
agent_token=$rotated_agent_token
|
||||
|
||||
step "persisted metrics"
|
||||
request -H "Authorization: Bearer $admin_token" "$MONITOR_BASE_URL/api/admin/servers" | \
|
||||
|
||||
@@ -7,10 +7,14 @@ const serverId = process.env.MONITOR_SERVER_ID;
|
||||
const latencyTaskId = process.env.MONITOR_LATENCY_TASK_ID;
|
||||
const expectTaskAssigned = process.env.MONITOR_EXPECT_TASK_ASSIGNED !== "0";
|
||||
const configOnly = process.env.MONITOR_CONFIG_ONLY === "1";
|
||||
const expectAgentRejected = process.env.MONITOR_EXPECT_AGENT_REJECTED === "1";
|
||||
const rotateAgentToken = process.env.MONITOR_ROTATE_AGENT_TOKEN === "1";
|
||||
|
||||
if (!baseUrl || !adminToken || !agentToken || !serverId || !latencyTaskId) {
|
||||
if (!baseUrl || !agentToken || (!expectAgentRejected && (!adminToken || !serverId || !latencyTaskId))) {
|
||||
throw new Error(
|
||||
"MONITOR_BASE_URL, MONITOR_ADMIN_TOKEN, MONITOR_AGENT_TOKEN, MONITOR_SERVER_ID and MONITOR_LATENCY_TASK_ID are required",
|
||||
expectAgentRejected
|
||||
? "MONITOR_BASE_URL and MONITOR_AGENT_TOKEN are required"
|
||||
: "MONITOR_BASE_URL, MONITOR_ADMIN_TOKEN, MONITOR_AGENT_TOKEN, MONITOR_SERVER_ID and MONITOR_LATENCY_TASK_ID are required",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,7 +163,35 @@ function closeSocket(socket) {
|
||||
});
|
||||
}
|
||||
|
||||
function waitForSocketClose(socket, expected) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cleanup = () => {
|
||||
clearTimeout(timer);
|
||||
socket.off("close", onClose);
|
||||
socket.off("error", onError);
|
||||
};
|
||||
const onClose = () => {
|
||||
cleanup();
|
||||
resolve();
|
||||
};
|
||||
const onError = (error) => {
|
||||
cleanup();
|
||||
reject(error);
|
||||
};
|
||||
const timer = setTimeout(() => {
|
||||
cleanup();
|
||||
reject(new Error(`WebSocket did not close after ${expected}`));
|
||||
}, 5_000);
|
||||
socket.once("close", onClose);
|
||||
socket.once("error", onError);
|
||||
});
|
||||
}
|
||||
|
||||
await expectSocketStatus("/api/agent/ws", "invalid-agent-token", 401);
|
||||
if (expectAgentRejected) {
|
||||
await expectSocketStatus("/api/agent/ws", agentToken, 401);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const dashboard = configOnly ? null : await openSocket("/api/ws", adminToken);
|
||||
let agent;
|
||||
@@ -168,6 +200,14 @@ try {
|
||||
const pong = waitForMessage(dashboard, "pong");
|
||||
dashboard.send("ping");
|
||||
await pong;
|
||||
|
||||
const oversizedDashboard = await openSocket("/api/ws", adminToken);
|
||||
const oversizedClosed = waitForSocketClose(
|
||||
oversizedDashboard,
|
||||
"an oversized dashboard message was sent",
|
||||
);
|
||||
oversizedDashboard.send("x".repeat(16 * 1024));
|
||||
await oversizedClosed;
|
||||
}
|
||||
|
||||
agent = await openSocket("/api/agent/ws", agentToken, { "CF-Connecting-IP": "8.8.8.8" });
|
||||
@@ -191,7 +231,48 @@ try {
|
||||
) {
|
||||
throw new Error(`Invalid Agent config: ${JSON.stringify(config)}`);
|
||||
}
|
||||
if (!configOnly) {
|
||||
if (rotateAgentToken) {
|
||||
const firstSocket = agent;
|
||||
const firstClosed = waitForSocketClose(firstSocket, "a duplicate Agent connected");
|
||||
const replacement = await openSocket("/api/agent/ws", agentToken);
|
||||
await waitForJsonMessage(
|
||||
replacement,
|
||||
"replacement Agent config",
|
||||
(message) => message.type === "config" && message.config,
|
||||
);
|
||||
await firstClosed;
|
||||
agent = replacement;
|
||||
|
||||
const rotatedClosed = waitForSocketClose(agent, "the Agent Token rotated");
|
||||
const rotateResponse = await fetch(
|
||||
new URL(`/api/admin/servers/${encodeURIComponent(serverId)}/token`, baseUrl),
|
||||
{
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${adminToken}` },
|
||||
},
|
||||
);
|
||||
if (!rotateResponse.ok) {
|
||||
throw new Error(
|
||||
`Agent Token rotation returned HTTP ${rotateResponse.status}: ${await rotateResponse.text()}`,
|
||||
);
|
||||
}
|
||||
const rotated = await rotateResponse.json();
|
||||
const rotatedToken = rotated.agent_token;
|
||||
if (!rotatedToken || rotatedToken === agentToken) {
|
||||
throw new Error("Agent Token rotation did not return a new token");
|
||||
}
|
||||
await rotatedClosed;
|
||||
await expectSocketStatus("/api/agent/ws", agentToken, 401);
|
||||
|
||||
const rotatedSocket = await openSocket("/api/agent/ws", rotatedToken);
|
||||
await waitForJsonMessage(
|
||||
rotatedSocket,
|
||||
"rotated Agent config",
|
||||
(message) => message.type === "config" && message.config,
|
||||
);
|
||||
await closeSocket(rotatedSocket);
|
||||
process.stdout.write(`${rotatedToken}\n`);
|
||||
} else if (!configOnly) {
|
||||
const wakeHintPromise = waitForJsonMessage(
|
||||
agent,
|
||||
"batched overview wake hint",
|
||||
@@ -318,7 +399,7 @@ try {
|
||||
ack.persistenceError !== false ||
|
||||
!Number.isInteger(ack.persistedThroughTs) ||
|
||||
ack.persistedThroughTs < latestTimestamp ||
|
||||
ack.nextD1WriteAfterMs !== 60_000 ||
|
||||
ack.nextPersistAfterMs !== 60_000 ||
|
||||
ack.nextWssReportAfterMs !== 5_000
|
||||
) {
|
||||
throw new Error(`Invalid Agent metric ACK: ${JSON.stringify(ack)}`);
|
||||
|
||||
Reference in new issue
Block a user