[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:
@@ -507,22 +507,34 @@ module.exports = function configureMiddleware(app, {
|
|||||||
|
|
||||||
// ── Sensitive routes: block external access when TOTP is off ──
|
// ── Sensitive routes: block external access when TOTP is off ──
|
||||||
// When TOTP is not enabled, all routes are open by design. But certain routes
|
// When TOTP is not enabled, all routes are open by design. But certain routes
|
||||||
// expose infrastructure details (config, tailscale, license keys) that should
|
// expose infrastructure details (config, tailscale, license keys, service
|
||||||
// not be accessible from the public internet. Block these from non-Tailscale IPs.
|
// manifests with container IDs, deployment manifests, port mappings) that
|
||||||
const SENSITIVE_ROUTES = [
|
// 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/config',
|
||||||
'/api/v1/tailscale/status',
|
'/api/v1/services',
|
||||||
'/api/v1/tailscale/devices',
|
'/api/v1/tailscale',
|
||||||
'/api/v1/updates/available',
|
'/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) => {
|
const sensitiveRouteMiddleware = (req, res, next) => {
|
||||||
if (!totpConfig.enabled) {
|
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))) {
|
if (isSensitive && !isTailscaleIP(getClientIP(req))) {
|
||||||
return res.status(403).json({
|
return errorResponse(res, 403, '[DC-122] Access denied. This endpoint requires TOTP authentication or Tailscale access.', {
|
||||||
success: false,
|
requiresTotp: true
|
||||||
error: 'This endpoint requires TOTP authentication or Tailscale access.'
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user