import fs from 'fs'; import { timingSafeEqual } from 'crypto'; import { FastifyRequest, FastifyReply } from 'fastify'; const SECRET_PATH = process.env.OPS_AGENT_SECRET_PATH ?? '/etc/ops-agent/secret'; let secretBuf: Buffer | null = null; export function loadSecret(): void { if (!fs.existsSync(SECRET_PATH)) { console.warn(`[auth] Secret file not found at ${SECRET_PATH} — auth disabled`); return; } const stat = fs.statSync(SECRET_PATH); if ((stat.mode & 0o177) !== 0) { console.warn(`[auth] Warning: ${SECRET_PATH} has loose permissions — expected 0640`); } secretBuf = Buffer.from(fs.readFileSync(SECRET_PATH, 'utf8').trim()); } export async function authHook(req: FastifyRequest, reply: FastifyReply): Promise { if (secretBuf === null) return; // auth disabled const header = req.headers['authorization'] ?? ''; const token = header.startsWith('Bearer ') ? header.slice(7) : ''; const tokenBuf = Buffer.from(token); const valid = tokenBuf.length === secretBuf.length && timingSafeEqual(tokenBuf, secretBuf); if (!valid) { await reply.status(401).send({ error: 'unauthorized' }); } }