#33: route the DNS verification endpoint to the service that serves it
The endpoint added in #32 lives on the protocols service, but the proxy only forwarded admin/dkim and admin/tls there - so admin/dns went to the auth service, which has no such route and answers 401 from its admin guard. The console would have reported "not authorised" for a request that was simply sent to the wrong process. It survived my own check because an unauthenticated probe of a nonexistent admin path returns 401 too: the guard runs before routing, so 401 proves nothing about whether a route exists. Comparing against a path I knew was fake is what exposed it. The list is now named and commented, and a test asserts every protocols-owned area appears in it, because the failure mode reads as a session problem rather than a routing one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e5edee096c
commit
e2ea58a4c9
2 files changed
+38
-4
No files matched your search
@@ -16,12 +16,23 @@ const AUTH_SERVICE_URL = process.env.AUTH_SERVICE_URL ?? "http://auth:3001";
|
||||
// sees one origin and one cookie either way.
|
||||
const PROTOCOLS_SERVICE_URL = process.env.PROTOCOLS_SERVICE_URL ?? "http://protocols:8080";
|
||||
|
||||
/**
|
||||
* Admin areas the protocols service owns rather than the auth service.
|
||||
*
|
||||
* Each is here because that process holds something the auth service does not:
|
||||
* the DKIM vault, the certificate watcher, and the DNS resolver plus the
|
||||
* expected-record spec.
|
||||
*
|
||||
* Adding a route to protocols without adding it here sends the request to the
|
||||
* auth service, which answers 401 from its admin guard - indistinguishable from
|
||||
* a session problem, and the reason to keep this list beside the services it
|
||||
* names.
|
||||
*/
|
||||
const PROTOCOLS_ADMIN_AREAS = new Set(["dkim", "tls", "dns"]);
|
||||
|
||||
function upstreamFor(path: string[]): string {
|
||||
// Mail reads and sends go to protocols, and so does DKIM: the private key
|
||||
// has to be sealed with that service's vault, which derives its key
|
||||
// differently from the auth service's.
|
||||
if (path[0] === "mail") return PROTOCOLS_SERVICE_URL;
|
||||
if (path[0] === "admin" && (path[1] === "dkim" || path[1] === "tls")) return PROTOCOLS_SERVICE_URL;
|
||||
if (path[0] === "admin" && PROTOCOLS_ADMIN_AREAS.has(path[1])) return PROTOCOLS_SERVICE_URL;
|
||||
return AUTH_SERVICE_URL;
|
||||
}
|
||||
|
||||
|
||||
@@ -172,3 +172,26 @@ describe("two-factor enrolment", () => {
|
||||
expect(source).toContain("onContinue");
|
||||
});
|
||||
});
|
||||
|
||||
describe("API proxy routing", () => {
|
||||
// A route added to the protocols service but not to the proxy's list is sent
|
||||
// to the auth service instead, which answers 401 from its admin guard. That
|
||||
// is indistinguishable from a session problem, so the failure looks like
|
||||
// "you are not logged in" rather than "this was routed to the wrong
|
||||
// service" - which is exactly how it went unnoticed once already.
|
||||
it("sends every protocols-owned admin area to the protocols service", () => {
|
||||
const proxy = readFileSync(resolve(process.cwd(), "src/app/api/v1/[...path]/route.ts"), "utf8");
|
||||
|
||||
// Areas the protocols service actually serves, taken from its router.
|
||||
for (const area of ["dkim", "tls", "dns"]) {
|
||||
expect(proxy).toContain(`"${area}"`);
|
||||
}
|
||||
expect(proxy).toMatch(/PROTOCOLS_ADMIN_AREAS/);
|
||||
});
|
||||
|
||||
it("still sends mail to protocols and everything else to auth", () => {
|
||||
const proxy = readFileSync(resolve(process.cwd(), "src/app/api/v1/[...path]/route.ts"), "utf8");
|
||||
expect(proxy).toMatch(/path\[0\] === "mail".*PROTOCOLS_SERVICE_URL/s);
|
||||
expect(proxy).toContain("return AUTH_SERVICE_URL");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user