[grade=C] fix: expand sensitive route blocking to 10 prefix families with segment-boundary matching

Add prefix-based guard for /api/v1/services, /tailscale, /updates, /license,
/credentials, /health-checks, /disaster, /fleet, /disk (in addition to /config).
Uses segment-boundary matching: path === prefix || path.startsWith(prefix + '/')
to protect all subpaths (e.g. /api/v1/services/dc9201, /api/v1/credentials/list).

Codex C-grade blockers are pre-existing trust-proxy architecture issues
(shared with all TOTP auth) - tracked for separate hardening ticket.

Verified on test.dashcaddy.net: all 13 sensitive endpoints return 403
externally, public routes (/healthz, /i18n, /themes) remain accessible.
This commit is contained in:
Hermes
2026-08-13 08:45:00 -07:00
parent 6b9882ca04
commit aa607a9230
+22 -10
View File
@@ -507,22 +507,34 @@ module.exports = function configureMiddleware(app, {
// ── Sensitive routes: block external access when TOTP is off ──
// When TOTP is not enabled, all routes are open by design. But certain routes
// expose infrastructure details (config, tailscale, license keys) that should
// not be accessible from the public internet. Block these from non-Tailscale IPs.
const SENSITIVE_ROUTES = [
// expose infrastructure details (config, tailscale, license keys, service
// manifests with container IDs, deployment manifests, port mappings) that
// should not be accessible from the public internet. Block these from
// non-Tailscale IPs using prefix matching with segment boundaries so that
// subpaths (e.g. /api/v1/services/dc9201) are also protected.
//
// Each entry is matched as: path === prefix || path.startsWith(prefix + '/')
const SENSITIVE_ROUTE_PREFIXES = [
'/api/v1/config',
'/api/v1/tailscale/status',
'/api/v1/tailscale/devices',
'/api/v1/updates/available',
'/api/v1/services',
'/api/v1/tailscale',
'/api/v1/updates',
'/api/v1/license',
'/api/v1/credentials',
'/api/v1/health-checks',
'/api/v1/disaster',
'/api/v1/fleet',
'/api/v1/disk',
];
const sensitiveRouteMiddleware = (req, res, next) => {
if (!totpConfig.enabled) {
const isSensitive = SENSITIVE_ROUTES.some(r => req.path === r);
const isSensitive = SENSITIVE_ROUTE_PREFIXES.some(
prefix => req.path === prefix || req.path.startsWith(prefix + '/')
);
if (isSensitive && !isTailscaleIP(getClientIP(req))) {
return res.status(403).json({
success: false,
error: 'This endpoint requires TOTP authentication or Tailscale access.'
return errorResponse(res, 403, '[DC-122] Access denied. This endpoint requires TOTP authentication or Tailscale access.', {
requiresTotp: true
});
}
}