fix(outline): stop the CA bundle being read as a server certificate #3

Merged
binjovi-bot merged 1 commit from fix/ssl-cert-file-collision into trunk 2026-09-14 13:42:40 +00:00
Owner

Outline deployed and will not boot:

Environment configuration is invalid, please check the following:

- SSL_CERT cannot be used without SSL_KEY.

Nothing in this repository sets SSL_CERT. The cluster's trust injection adds this to every pod:

SSL_CERT_FILE=/etc/ssl/cluster-trust/ca-bundle.crt
NODE_EXTRA_CA_CERTS=/etc/ssl/cluster-trust/ca-bundle.crt
REQUESTS_CA_BUNDLE=...  GIT_SSL_CAINFO=...  NIX_SSL_CERT_FILE=...

To OpenSSL, SSL_CERT_FILE means "the trust store is here" — which certificates to believe.

Outline reads its environment through a Docker-secrets proxy (server/utils/environment.ts): reading NAME when it is undefined returns the contents of the file named by NAME_FILE. So Outline read the cluster CA bundle and concluded the operator had supplied a TLS server certificate to terminate with. There is no SSL_KEY to go with it, and @CannotUseWithout("SSL_KEY") ends the process before it serves.

Two conventions, one name.

The fix

The proxy falls back only when the value is strictly undefined:

const value = target[prop];
if (value !== undefined) { return value; }
const filePath = target[`${prop}_FILE`];

so declaring SSL_CERT at all — even empty — stops the fallback, and Outline's own toOptionalString("") turns it back into undefined for validation. Node still trusts the bundle through NODE_EXTRA_CA_CERTS, which is the variable Node actually reads; SSL_CERT_FILE was never doing that job here.

Worth knowing beyond this repository

Any application in this cluster that implements the _FILE convention meets the same collision. The reason is written down in both the manifest and the README rather than left as an empty value someone will later tidy away as dead configuration.

https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3

Outline deployed and will not boot: ``` Environment configuration is invalid, please check the following: - SSL_CERT cannot be used without SSL_KEY. ``` Nothing in this repository sets `SSL_CERT`. The cluster's trust injection adds this to every pod: ``` SSL_CERT_FILE=/etc/ssl/cluster-trust/ca-bundle.crt NODE_EXTRA_CA_CERTS=/etc/ssl/cluster-trust/ca-bundle.crt REQUESTS_CA_BUNDLE=... GIT_SSL_CAINFO=... NIX_SSL_CERT_FILE=... ``` To OpenSSL, `SSL_CERT_FILE` means *"the trust store is here"* — which certificates to **believe**. Outline reads its environment through a Docker-secrets proxy (`server/utils/environment.ts`): reading `NAME` when it is `undefined` returns the contents of the file named by `NAME_FILE`. So Outline read the cluster CA bundle and concluded the operator had supplied a TLS **server** certificate to terminate with. There is no `SSL_KEY` to go with it, and `@CannotUseWithout("SSL_KEY")` ends the process before it serves. Two conventions, one name. ## The fix The proxy falls back only when the value is strictly `undefined`: ```js const value = target[prop]; if (value !== undefined) { return value; } const filePath = target[`${prop}_FILE`]; ``` so declaring `SSL_CERT` at all — even empty — stops the fallback, and Outline's own `toOptionalString("")` turns it back into `undefined` for validation. Node still trusts the bundle through `NODE_EXTRA_CA_CERTS`, which is the variable Node actually reads; `SSL_CERT_FILE` was never doing that job here. ## Worth knowing beyond this repository Any application in this cluster that implements the `_FILE` convention meets the same collision. The reason is written down in both the manifest and the README rather than left as an empty value someone will later tidy away as dead configuration. https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
fix(outline): stop the CA bundle being read as a server certificate
Some checks failed
binjovi/ci Binjovi will not build this: its base branch must be trunk. Retarget the pull request.
bd79a32d2c
Outline will not boot:

    Environment configuration is invalid, please check the following:
    - SSL_CERT cannot be used without SSL_KEY.

Nothing here sets SSL_CERT. The cluster injects
SSL_CERT_FILE=/etc/ssl/cluster-trust/ca-bundle.crt into every pod, which
to OpenSSL means "the trust store is here" -- which certificates to
BELIEVE. Outline reads its environment through a Docker-secrets proxy
(server/utils/environment.ts): reading NAME when it is `undefined` returns
the contents of the file named by NAME_FILE. So Outline loaded the cluster
CA bundle and concluded the operator had supplied a TLS SERVER certificate
to terminate with. There is no SSL_KEY to go with it, and
@CannotUseWithout("SSL_KEY") ends the process before it serves.

Two conventions, one name. The proxy falls back only when the value is
strictly `undefined`, so declaring SSL_CERT at all -- even empty -- stops
the fallback, and Outline's own toOptionalString("") turns it back into
`undefined` for validation. Node still trusts the bundle through
NODE_EXTRA_CA_CERTS, the variable Node actually reads; SSL_CERT_FILE was
never doing that job here.

Any other application in this cluster that implements the `_FILE`
convention meets the same collision, so the reason is written down in both
the manifest and the README rather than left as an empty value someone
will tidy away.

Claude-Session: https://claude.ai/code/session_01JGe4pyhQ36AhgDfPJry4d3
binjovi-bot deleted branch fix/ssl-cert-file-collision 2026-09-14 13:42:40 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sean/outline!3
No description provided.