diff --git a/.env.example b/.env.example index 19b5b08..1214e8b 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,3 @@ JWT_ACCESS_SECRET=change-me-access JWT_REFRESH_SECRET=change-me-refresh JWT_ACCESS_EXPIRES_IN=15m JWT_REFRESH_EXPIRES_IN=30d - -# Center <-> Guild handshake -CENTER_SHARED_SECRET=change-me-center-secret diff --git a/src/main.ts b/src/main.ts index a4456db..7776e11 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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'); } diff --git a/src/nodes/nodes.controller.ts b/src/nodes/nodes.controller.ts index 3f73188..7716fca 100644 --- a/src/nodes/nodes.controller.ts +++ b/src/nodes/nodes.controller.ts @@ -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 }, });