obsidian/wiki/concepts/nodejs-ssl-system-trust-store.md
2026-04-19 22:03:39 +01:00

85 lines
4.2 KiB
Markdown

---
title: "Node.js SSL — System Trust Store Is Ignored"
aliases: [nodejs-ssl, nodejs-trust-store, node-tls-reject, nodejs-self-signed]
tags: [nodejs, ssl, tls, systemd, lxc, docker, homelab]
sources:
- "daily/2026-04-19.md"
created: 2026-04-19
updated: 2026-04-19
---
# Node.js SSL — System Trust Store Is Ignored
Node.js does not use the operating system's certificate trust store. Adding a self-signed certificate to `/etc/ssl/certs/` or running `update-ca-certificates` has no effect on Node.js TLS verification. The only reliable fix is to set `NODE_TLS_REJECT_UNAUTHORIZED=0` as an environment variable on the Node.js process itself — and for systemd-managed services, this must be in the unit file, not in a shell profile.
## Key Points
- **Node.js bundles its own CA store** (Mozilla's root CAs compiled in) — it does not read `/etc/ssl/certs/` at runtime
- `update-ca-certificates` and adding certs to the Linux trust store does not affect Node.js
- `NODE_TLS_REJECT_UNAUTHORIZED=0` must be set in the **process environment**, not a login shell profile — for systemd services this means the unit file
- Setting the env var via shell (`export NODE_TLS_REJECT_UNAUTHORIZED=0`) only affects that shell and its children — not a systemd service started separately
- For self-signed or internal CA certificates, the proper long-term fix is `NODE_EXTRA_CA_CERTS=/path/to/ca.crt` — this tells Node.js to trust additional CAs without disabling verification entirely
## Details
### Why System Trust Store Doesn't Work
Unlike Python (`certifi` or system certs), Go (uses system certs by default), or curl (uses system certs), Node.js compiles Mozilla's CA bundle at build time and uses it exclusively. This is a deliberate design decision for portability. Even in a Docker container or LXC where the system certs are managed by the distro, Node.js won't see them.
The consequence: any application talking to an internal service with a self-signed or internal-CA-signed certificate will get `CERT_HAS_EXPIRED`, `UNABLE_TO_VERIFY_LEAF_SIGNATURE`, or `SELF_SIGNED_CERT_IN_CHAIN` errors even after you've properly configured the OS trust store.
### The systemd Fix
For services managed by systemd, the env var must be in the unit file:
```ini
# /etc/systemd/system/homepage.service
[Service]
Environment=NODE_TLS_REJECT_UNAUTHORIZED=0
ExecStart=/usr/bin/node /opt/homepage/server.js
```
After editing:
```bash
systemctl daemon-reload
systemctl restart homepage
```
A `drop-in` override is cleaner than editing the unit directly:
```bash
mkdir -p /etc/systemd/system/homepage.service.d/
cat > /etc/systemd/system/homepage.service.d/tls.conf << 'EOF'
[Service]
Environment=NODE_TLS_REJECT_UNAUTHORIZED=0
EOF
systemctl daemon-reload && systemctl restart homepage
```
### The Proper Fix (NODE_EXTRA_CA_CERTS)
`NODE_TLS_REJECT_UNAUTHORIZED=0` disables all certificate validation — dangerous on public-facing services. The correct approach for internal CAs:
```bash
# Export Proxmox's self-signed certificate
openssl s_client -connect 192.168.1.100:8006 -showcerts </dev/null 2>/dev/null \
| openssl x509 -outform PEM > /etc/ssl/private/proxmox-ca.crt
# Tell Node.js to trust it
Environment=NODE_EXTRA_CA_CERTS=/etc/ssl/private/proxmox-ca.crt
```
This adds the cert to Node.js's trusted set without disabling verification for other connections.
### Context: Homepage in LXC (2026-04-19)
Homepage (a self-hosted dashboard) running in LXC 103 needed to call the Proxmox API at `https://192.168.1.100:8006`. Proxmox uses a self-signed certificate. The Proxmox cert was added to the system trust store inside LXC 103 — no effect. `NODE_TLS_REJECT_UNAUTHORIZED=0` was set in the shell — no effect (systemd started the service separately). Only adding it to the systemd unit file resolved the issue.
## Related Concepts
- [[wiki/concepts/homepage-proxmox-widget-quirks]] — the Homepage dashboard context where this was discovered
- [[wiki/concepts/lxc-arp-cache-api-failures]] — the other root cause of Homepage widget failures on that server
- [[wiki/homelab/_index]] — Proxmox and LXC setup context
## Sources
- [[daily/2026-04-19.md]] — Homepage LXC 103 calling Proxmox API; cert added to system trust store had no effect; `NODE_TLS_REJECT_UNAUTHORIZED=0` in systemd unit file was the only fix that worked