The certificate is genuine, from Mesh Internal CA, and nothing on the workstation trusts it — verbatim the error the report gives. The public name on the same proxy verifies cleanly, which puts the fault exactly where the report puts it. What is in the way is not an assignment. `ca-trust` is merged in the catalogue and has never been registered with the mesh — 39 of 76 manifests are — so there is no module to assign. It dry-runs clean and needs no artifact built. Two findings from reproducing it, both their own issues: 157 — every routed name is published with an `.internal` alias that nothing serves. The hosts file says keycloak.novox.be.internal; the proxy serves keycloak.novox.internal and refuses the other by name. The first three names I tried came from the hosts file and failed with a TLS alert rather than a verification error, which pointed at a regression that had not happened. 158 — the proxy re-logs all 52 routes every two seconds, 31 times a minute. The one line that explained 157 sat between two of them. Also recorded, because it was nearly filed as a defect and is not one: step-ca publishes roots as /roots.pem, which is PEM, so ca-trust's fetch and its refuse-a-non-certificate guard are both right. Its other endpoint /roots returns JSON that contains the text the guard greps for, so the guard is sound only because of which path is published.
64 lines
2.9 KiB
Markdown
64 lines
2.9 KiB
Markdown
---
|
|
status: located
|
|
opened: 2026-09-30
|
|
located-in:
|
|
- mesh-controller internal/catalogue/roster.go (the roster's entries for a routed name)
|
|
fixed-by:
|
|
amended-design:
|
|
---
|
|
|
|
# 157 — A routed name is published with an `.internal` alias that nothing serves
|
|
|
|
## What was observed
|
|
|
|
Every machine's hosts file carries two entries for each routed name — the name, and the name with
|
|
`.internal` appended:
|
|
|
|
```
|
|
10.10.0.1 keycloak.novox.be.internal keycloak.novox.be
|
|
10.10.0.1 drive.novox.be.internal drive.novox.be
|
|
10.10.0.1 umami.novox.be.internal umami.novox.be
|
|
```
|
|
|
|
The suffixed one resolves and is served by nothing. The proxy refuses it during the handshake, and
|
|
says so exactly:
|
|
|
|
```
|
|
http: TLS handshake error from 10.10.0.3:33480:
|
|
no public route for "keycloak.novox.be.internal" in this mesh, so no certificate is asked for
|
|
```
|
|
|
|
A client sees `curl: (35) TLS connect error ... tlsv1 alert internal error` and no peer certificate —
|
|
a server-side refusal, with nothing in it to say the name was never real.
|
|
|
|
The name the proxy does serve is `<label>.<node>.internal` — `keycloak.novox.internal` — which is what
|
|
[issue 139](../139-an-internal-route-name-resolves-to-the-consumers-node/00-report.md) describes. So
|
|
there are two internal shapes for one service, one of them composed by appending the suffix to a name
|
|
that already has a domain.
|
|
|
|
## Why it matters
|
|
|
|
**It sends a reader to the wrong diagnosis.** Reproducing
|
|
[issue 129](../129-nothing-makes-a-machine-trust-the-meshs-authority/00-report.md) on 2026-09-30, the
|
|
first three names tried came from the hosts file, all failed with a TLS alert rather than the
|
|
verification error 129 reports, and the evidence pointed at the proxy having lost its internal
|
|
certificates — a regression that had not happened. The correct name reproduces 129 exactly. Several
|
|
minutes went into a fault that did not exist, and the only thing that distinguished the two was reading
|
|
the proxy's own log.
|
|
|
|
It is also a name in every machine's hosts file, and in every container's, that cannot be reached: the
|
|
shape the mesh is otherwise careful about — writing a name that resolves to something that does not
|
|
answer is worse than not writing it, because a connection to an address that does not answer hangs
|
|
where a name that does not resolve fails at once ([ADR 0007](../../02-DECISIONS/0007-connectivity.md),
|
|
and the same reasoning in `namesInTheMesh`).
|
|
|
|
## Where to look
|
|
|
|
The roster template renders one entry per name as `{{.FQDN}} {{.Name}}`, and `FQDN` is composed by
|
|
appending the mesh suffix to the bare name. For a node that is right — `novox` becomes
|
|
`novox.internal`. For a routed name the bare name is already a fully qualified public name, so the
|
|
composition produces `keycloak.novox.be.internal`, which is not a name anything was told to serve.
|
|
|
|
The fix is a judgement about what a routed name's internal form is, and 139 is the record that asks it;
|
|
this one is the evidence that the current answer publishes a third thing that is neither.
|