85 lines
4.2 KiB
Markdown
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
|