Skip to content

One unreadable TLS certificate discards the entire saved routing table #233

Description

@abarrantes

Summary

When kamal-proxy restores its saved state on boot, a single unreadable TLS
certificate causes it to discard the entire routing table, not just the
affected service. On a host running several services behind one proxy, one app's
missing cert takes every other app offline at once.

Observed on basecamp/kamal-proxy:v0.9.2.

What happened

Four apps shared one proxy on a single host. One of them was deployed by a
different SSH user than the other three. Since Kamal bind-mounts the proxy's
apps-config directory from the deploying user's home:

--volume $PWD/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config

that deploy rebooted the proxy with the mount pointing at a different home
directory. The other three apps' certificates were still on disk, but at a path
the proxy could no longer see.

On boot:

{"level":"ERROR","msg":"Error loading TLS certificate","error":"open /home/kamal-proxy/.apps-config/app-a/tls/web/cert.pem: no such file or directory"}
{"level":"ERROR","msg":"Failed to decode saved state","path":"/home/kamal-proxy/.config/kamal-proxy/kamal-proxy.state","error":"unable to load certificate"}
{"level":"INFO","msg":"Server started","http":80,"https":443}

After that, kamal-proxy list returned an empty table and every hostname failed
TLS with unknown server name — including the two services whose certificates
were perfectly intact and whose containers were healthy and serving 200 on
/up the whole time. Behind Cloudflare this surfaced as HTTP 525 across all
sites simultaneously.

The misconfiguration was ours. What turned it into a multi-app outage is that
one bad cert invalidated the whole state file.

Expected

A service whose certificate can't be loaded should fail in isolation: log the
error, drop or mark that one service, and restore the remaining services from
the saved state.

Actual

Failed to decode saved state discards all routes. Every service on the host
goes down, and there is no partial recovery — each app has to be re-registered
manually.

Why it's hard to diagnose

The failure is silent after startup and maximally misleading: the apps are up,
their containers are healthy, their own certificates are fine, and nothing in
their logs indicates a problem. Only the two ERROR lines at proxy boot — which
scroll away — point at the cause.

Suggested fix

Make state restoration per-service rather than all-or-nothing. Skipping the
services that fail to load, and logging one warning per skipped service, would
have kept this to a single-app outage.

If the all-or-nothing behavior is intentional, it would help a lot to surface it
after boot too — e.g. having kamal-proxy list report that a saved state exists
but was rejected, instead of printing an empty table indistinguishable from a
fresh install.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions