refactor(center): local-only guild register endpoint without shared secret

This commit is contained in:
nav
2026-05-13 08:41:45 +00:00
parent 1c07f43032
commit 0a4cb62065
3 changed files with 11 additions and 30 deletions

View File

@@ -20,7 +20,6 @@ function validateEnv(): void {
requireEnv('DB_USER');
requireEnv('DB_PASSWORD');
requireEnv('DB_NAME');
requireEnv('CENTER_SHARED_SECRET');
requireEnv('JWT_ACCESS_SECRET');
requireEnv('JWT_REFRESH_SECRET');
}

View File

@@ -13,6 +13,7 @@ import {
Patch,
Post,
Query,
Req,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@@ -22,12 +23,6 @@ import { GuildNode } from '../entities/guild-node.entity';
import { AuditService } from '../audit/audit.service';
import { RegisterNodeDto } from './dto.register-node.dto';
import { UpdateNodeStatusDto } from './dto.update-node-status.dto';
import {
buildCanonical,
safeEqualHex,
signCanonical,
verifyRequestTime,
} from '../common/hmac';
import { FABRIC_PROTOCOL_VERSION, normalizeVersion } from '../common/version';
@Controller('nodes')
@@ -40,12 +35,19 @@ export class NodesController {
@Post('register')
async register(
@Req() req: { ip?: string; socket?: { remoteAddress?: string } },
@Body() body: RegisterNodeDto,
@Headers('x-fabric-signature') signature?: string,
@Headers('x-fabric-timestamp') timestamp?: string,
@Headers('x-fabric-nonce') nonce?: string,
@Headers('x-fabric-version') fabricVersion?: string,
) {
const remoteAddress = (req.ip ?? req.socket?.remoteAddress ?? '').toLowerCase();
const isLoopback =
remoteAddress === '127.0.0.1' ||
remoteAddress === '::1' ||
remoteAddress === '::ffff:127.0.0.1';
if (!isLoopback) {
throw new ForbiddenException('register endpoint only allows localhost caller');
}
const requestedVersion = normalizeVersion(fabricVersion);
if (requestedVersion !== FABRIC_PROTOCOL_VERSION) {
throw new HttpException(
@@ -61,23 +63,6 @@ export class NodesController {
);
}
const secret = process.env.CENTER_SHARED_SECRET as string;
if (!signature || !timestamp || !nonce || !verifyRequestTime(timestamp)) {
throw new ForbiddenException('invalid hmac headers');
}
const canonical = buildCanonical({
method: 'POST',
path: '/api/nodes/register',
timestamp,
nonce,
body: JSON.stringify(body),
});
const expected = signCanonical(secret, canonical);
if (!safeEqualHex(signature, expected)) {
throw new ForbiddenException('invalid shared secret');
}
const existedByNodeId = await this.nodeRepo.findOne({
where: { nodeId: body.nodeId },
});