mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
Distrust compromised eMudhra intermediates in certifi and Deno
The private keys of eight eMudhra intermediate CAs can be recovered from their certificates, so anyone can mint a trusted certificate for any host. * Require certifi>=2026.10.19; yt-dlp, requests and curl-cffi use its bundle, and 2026.10.19 removes the emSign ECC Root CA - G3 root they chain to. * Set DENO_TLS_CA_STORE=system for the API (systemd + Docker) and the archive container so Deno follows the apt-patched system store instead of roots that only change with a Deno upgrade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
22afb81879
commit
4f66472492
6 changed files with 44 additions and 0 deletions
|
|
@ -1,5 +1,7 @@
|
|||
FROM python:3.13-trixie
|
||||
ENV DOCKER=true
|
||||
# Deno trusts the system store, which apt keeps current, instead of its bundled roots.
|
||||
ENV DENO_TLS_CA_STORE=system
|
||||
WORKDIR /opt/wrolpi
|
||||
|
||||
# Update dependencies for the services install.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ WORKDIR /app
|
|||
|
||||
# Install Python for main.py
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
# Deno trusts the system store, which apt keeps current, instead of its bundled roots.
|
||||
ENV DENO_TLS_CA_STORE=system
|
||||
RUN apt update && apt install -y python3 python3-dev python3-venv curl unzip && ln -sf python3 /usr/bin/python
|
||||
RUN python3 -m venv /app/venv
|
||||
RUN . /app/venv/bin/activate
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ Conflicts=wrolpi-api.service
|
|||
Environment=PYTHONUNBUFFERED=1
|
||||
# Sanic 25.x wants 'spawn' but our code uses 'fork' for multiprocessing.
|
||||
Environment=SANIC_START_METHOD_SET=1
|
||||
# Deno (single-file, yt-dlp EJS) trusts the system store, which apt keeps current, instead of its
|
||||
# bundled roots.
|
||||
Environment=DENO_TLS_CA_STORE=system
|
||||
# Required for yt-dlp to access GNOME keyring for browser cookie decryption.
|
||||
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus
|
||||
ExecStart=/opt/wrolpi/venv/bin/python -m sanic main:api_app --host 0.0.0.0 --port 8081 -w 2 --reload --debug
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ Conflicts=wrolpi-api-dev.service
|
|||
Environment=PYTHONUNBUFFERED=1
|
||||
# Sanic 25.x wants 'spawn' but our code uses 'fork' for multiprocessing.
|
||||
Environment=SANIC_START_METHOD_SET=1
|
||||
# Deno (single-file, yt-dlp EJS) trusts the system store, which apt keeps current, instead of its
|
||||
# bundled roots.
|
||||
Environment=DENO_TLS_CA_STORE=system
|
||||
# Required for yt-dlp to access GNOME keyring for browser cookie decryption.
|
||||
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus
|
||||
ExecStart=/opt/wrolpi/venv/bin/python -O -m sanic main:api_app --host 0.0.0.0 --port 8081 -w 5
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ aiohttp==3.14.4
|
|||
alembic==1.20.0
|
||||
beautifulsoup4==4.15.0
|
||||
cachetools==7.2.1
|
||||
# yt-dlp, requests and curl-cffi trust certifi's bundle, not the system store. 2026.10.19 removes
|
||||
# the root of the eMudhra intermediates whose private keys are recoverable.
|
||||
certifi>=2026.10.19
|
||||
cryptography==50.0.2
|
||||
ebooklib==0.20
|
||||
feedparser==6.0.14
|
||||
|
|
|
|||
31
wrolpi/test/test_trust_store.py
Normal file
31
wrolpi/test/test_trust_store.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"""
|
||||
Tests that WROLPi's TLS clients do not trust compromised certificate authorities.
|
||||
"""
|
||||
import certifi
|
||||
import pytest
|
||||
|
||||
from wrolpi.vars import PROJECT_DIR
|
||||
|
||||
# Roots of the eMudhra intermediates whose private keys can be recovered from their certificates.
|
||||
DISTRUSTED_ROOT_LABELS = (
|
||||
'emSign ECC Root CA - G3',
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('label', DISTRUSTED_ROOT_LABELS)
|
||||
def test_certifi_distrusts_root(label):
|
||||
"""certifi's bundle (used by yt-dlp, requests and curl-cffi) must not contain a distrusted root."""
|
||||
labels = [line.split('"')[1] for line in certifi.contents().splitlines() if line.startswith('# Label: "')]
|
||||
assert labels, 'certifi bundle has no labels; cannot verify distrusted roots are absent'
|
||||
assert label not in labels
|
||||
|
||||
|
||||
@pytest.mark.parametrize('path,setting', [
|
||||
('etc/raspberrypios/wrolpi-api.service', 'Environment=DENO_TLS_CA_STORE=system'),
|
||||
('etc/raspberrypios/wrolpi-api-dev.service', 'Environment=DENO_TLS_CA_STORE=system'),
|
||||
('docker/api/Dockerfile', 'ENV DENO_TLS_CA_STORE=system'),
|
||||
('docker/archive/Dockerfile', 'ENV DENO_TLS_CA_STORE=system'),
|
||||
])
|
||||
def test_deno_uses_system_trust_store(path, setting):
|
||||
"""Deno's bundled roots only change with a Deno upgrade; the system store is patched by apt."""
|
||||
assert setting in (PROJECT_DIR / path).read_text().splitlines()
|
||||
Loading…
Add table
Add a link
Reference in a new issue