From 1c6bfb56aaf16f2927aa7ae6df7a47c37d10a4f2 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 12:55:48 -0700 Subject: [PATCH 1/6] fix: refresh code interpreter certificate trust stores --- .tool-versions | 2 +- template/requirements.txt | 1 + template/server/requirements.txt | 1 + template/template.py | 6 +++++ tests/test_certificates.py | 41 ++++++++++++++++++++++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/test_certificates.py diff --git a/.tool-versions b/.tool-versions index 08bec1cb..f45c708f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ python 3.11 -uv 0.10.0 +uv 0.13.4 # Keep in sync with `packageManager` in package.json — pnpm self-switches to # that version at runtime. pnpm 10.34.5 diff --git a/template/requirements.txt b/template/requirements.txt index 3e453aa1..da4ae1a3 100644 --- a/template/requirements.txt +++ b/template/requirements.txt @@ -42,3 +42,4 @@ urllib3==2.7.0 xarray==2025.4.0 xlrd==2.0.2 sympy==1.14.0 +certifi==2026.10.19 diff --git a/template/server/requirements.txt b/template/server/requirements.txt index b3e44b71..6d99e0d8 100644 --- a/template/server/requirements.txt +++ b/template/server/requirements.txt @@ -4,3 +4,4 @@ websockets==12.0 uvicorn[standard]==0.30.1 requests==2.33.0 pydantic==2.9.1 +certifi==2026.10.19 diff --git a/template/template.py b/template/template.py index fd8dd937..aaaf9ed1 100644 --- a/template/template.py +++ b/template/template.py @@ -37,6 +37,11 @@ def make_template( "ca-certificates", ] ) + .run_cmd( + r"sed -i -E '/^mozilla\/(emSign_ECC_Root_CA_-_G3|Comodo_AAA_Services_root)\.crt$/s/^/!/' /etc/ca-certificates.conf" + " && update-ca-certificates --fresh" + ) + .run_cmd("python -m pip install --upgrade pip==26.2.2") .run_cmd("curl -fsSL https://deb.nodesource.com/setup_20.x | bash -") .apt_install("nodejs") .copy("requirements.txt", "requirements.txt") @@ -88,6 +93,7 @@ def make_template( # Create server virtual environment .copy("server", ".server") .run_cmd("python -m venv .server/.venv") + .run_cmd(".server/.venv/bin/python -m pip install --upgrade pip==26.2.2") # Copy and install server requirements .run_cmd( ".server/.venv/bin/pip install --no-cache-dir -r .server/requirements.txt" diff --git a/tests/test_certificates.py b/tests/test_certificates.py new file mode 100644 index 00000000..ab34b92f --- /dev/null +++ b/tests/test_certificates.py @@ -0,0 +1,41 @@ +import shlex + +import pytest +from e2b import Sandbox + +pytestmark = pytest.mark.skip_debug + +CHECK_CERTIFICATES = """ +import ssl +from importlib.metadata import version + +import certifi +from pip._vendor import certifi as pip_certifi + +assert tuple(map(int, version('certifi').split('.'))) >= (2026, 10, 19) +assert tuple(map(int, version('pip').split('.'))) >= (26, 2, 2) + +blocked = {'emSign ECC Root CA - G3', 'AAA Certificate Services'} +for bundle in (None, certifi.where(), pip_certifi.where()): + context = ssl.create_default_context(cafile=bundle) + certificates = context.get_ca_certs() + assert certificates, f'Empty trust store: {bundle}' + names = { + value + for certificate in certificates + for rdn in certificate['subject'] + for key, value in rdn + if key == 'commonName' + } + assert not names & blocked, f'{bundle}: still trusts {names & blocked}' +print('Certificate stores verified') +""" + + +@pytest.mark.parametrize("python", ["python", "/root/.server/.venv/bin/python"]) +def test_certificate_stores(sandbox: Sandbox, python: str): + result = sandbox.commands.run( + f"{python} -c {shlex.quote(CHECK_CERTIFICATES)}", user="root" + ) + assert result.exit_code == 0 + assert "Certificate stores verified" in result.stdout From d8dd71c1e64ba60f0ced95d254c823f2f88c17db Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 13:09:25 -0700 Subject: [PATCH 2/6] fix: use system certificate trust for Node.js --- template/start-up.sh | 2 ++ template/systemd/jupyter.service | 1 + template/template.py | 1 + tests/test_certificates.py | 46 ++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/template/start-up.sh b/template/start-up.sh index 0ea0494d..0379b6f7 100755 --- a/template/start-up.sh +++ b/template/start-up.sh @@ -1,5 +1,7 @@ #!/bin/bash +export NODE_OPTIONS=--use-openssl-ca + install -d -m 0700 /run/e2b-jupyter function start_code_interpreter() { diff --git a/template/systemd/jupyter.service b/template/systemd/jupyter.service index 178124bd..a55040b1 100644 --- a/template/systemd/jupyter.service +++ b/template/systemd/jupyter.service @@ -8,6 +8,7 @@ StartLimitBurst=0 Type=simple RuntimeDirectory=e2b-jupyter RuntimeDirectoryMode=0700 +Environment=NODE_OPTIONS=--use-openssl-ca Environment=MATPLOTLIBRC=/root/.config/matplotlib/.matplotlibrc ExecStart=/usr/local/bin/jupyter server --IdentityProvider.token="" ExecStartPost=-/usr/bin/systemctl reset-failed code-interpreter diff --git a/template/template.py b/template/template.py index aaaf9ed1..d8607ccf 100644 --- a/template/template.py +++ b/template/template.py @@ -17,6 +17,7 @@ def make_template( .set_envs( { "PIP_DEFAULT_TIMEOUT": "100", + "NODE_OPTIONS": "--use-openssl-ca", "PIP_DISABLE_PIP_VERSION_CHECK": "1", "PIP_NO_CACHE_DIR": "1", "JAVA_VERSION": "11", diff --git a/tests/test_certificates.py b/tests/test_certificates.py index ab34b92f..424b8418 100644 --- a/tests/test_certificates.py +++ b/tests/test_certificates.py @@ -1,8 +1,11 @@ +import json import shlex import pytest from e2b import Sandbox +from harness import CodeInterpreter + pytestmark = pytest.mark.skip_debug CHECK_CERTIFICATES = """ @@ -39,3 +42,46 @@ def test_certificate_stores(sandbox: Sandbox, python: str): ) assert result.exit_code == 0 assert "Certificate stores verified" in result.stdout + + +CHECK_NODE_HTTPS = """ +(async () => { +const https = require('node:https'); +const probe = (host) => new Promise((resolve, reject) => { + const request = https.get(`https://${host}/`, (response) => { + response.resume(); + resolve('connected'); + }); + request.setTimeout(15000, () => request.destroy(new Error('TLS probe timed out'))); + request.on('error', (error) => { + if (error.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' || + error.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' || + error.code === 'SELF_SIGNED_CERT_IN_CHAIN') { + resolve('untrusted'); + } else { + reject(error); + } + }); +}); +const results = await Promise.all([ + probe('dv-g3b-emudhra.we-have-a-problem.com'), + probe('dv-g3b-aaa-emudhra.we-have-a-problem.com'), + probe('nodejs.org'), +]); +console.log(JSON.stringify(results)); +})() +""" + + +def test_node_certificate_stores(sandbox: Sandbox, client: CodeInterpreter): + command = CHECK_NODE_HTTPS + result = sandbox.commands.run(f"node -e {shlex.quote(command)}") + assert json.loads(result.stdout) == ["untrusted", "untrusted", "connected"] + + execution = client.run_code("await " + command, language="js") + assert execution.error is None + assert json.loads("".join(execution.stdout)) == [ + "untrusted", + "untrusted", + "connected", + ] From fe09f19ff88f1cb7e9f69eb173c97057440bb0cb Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 13:22:54 -0700 Subject: [PATCH 3/6] fix: persist Node trust settings and refresh Java certificates --- template/template.py | 4 ++++ tests/test_certificates.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/template/template.py b/template/template.py index d8607ccf..2abc3f74 100644 --- a/template/template.py +++ b/template/template.py @@ -45,6 +45,9 @@ def make_template( .run_cmd("python -m pip install --upgrade pip==26.2.2") .run_cmd("curl -fsSL https://deb.nodesource.com/setup_20.x | bash -") .apt_install("nodejs") + .run_cmd( + "echo 'export NODE_OPTIONS=--use-openssl-ca' > /etc/profile.d/node-ca.sh" + ) .copy("requirements.txt", "requirements.txt") .pip_install("--no-cache-dir -r requirements.txt") ) @@ -80,6 +83,7 @@ def make_template( [ "mkdir -p /usr/lib/jvm", "curl -fsSL https://download.java.net/java/ga/jdk${JAVA_VERSION}/openjdk-${JAVA_VERSION}_linux-x64_bin.tar.gz | tar -xz -C /usr/lib/jvm", + "/usr/lib/jvm/jdk-${JAVA_VERSION}/bin/keytool -delete -cacerts -storepass changeit -alias 'comodoaaaca [jdk]'", "update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-${JAVA_VERSION}/bin/java 1", "update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-${JAVA_VERSION}/bin/javac 1", "wget https://github.com/SpencerPark/IJava/releases/download/v${IJAVA_VERSION}/ijava-${IJAVA_VERSION}.zip", diff --git a/tests/test_certificates.py b/tests/test_certificates.py index 424b8418..06484089 100644 --- a/tests/test_certificates.py +++ b/tests/test_certificates.py @@ -85,3 +85,14 @@ def test_node_certificate_stores(sandbox: Sandbox, client: CodeInterpreter): "untrusted", "connected", ] + + +def test_java_certificate_store(sandbox: Sandbox): + result = sandbox.commands.run( + "/usr/lib/jvm/jdk-11/bin/keytool -J-Duser.language=en -list -v -cacerts -storepass changeit", + user="root", + ) + assert result.exit_code == 0 + assert "trustedCertEntry" in result.stdout + assert "CN=AAA Certificate Services," not in result.stdout + assert "CN=emSign ECC Root CA - G3," not in result.stdout From b7c642962873e16db871c80ca31264790b0151b6 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 13:50:21 -0700 Subject: [PATCH 4/6] fix: clean certificate directory and secure pip upgrades --- template/start-up.sh | 2 +- template/template.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/template/start-up.sh b/template/start-up.sh index 0379b6f7..3c6f11ed 100755 --- a/template/start-up.sh +++ b/template/start-up.sh @@ -1,6 +1,6 @@ #!/bin/bash -export NODE_OPTIONS=--use-openssl-ca +export NODE_OPTIONS="--use-openssl-ca${NODE_OPTIONS:+ $NODE_OPTIONS}" install -d -m 0700 /run/e2b-jupyter diff --git a/template/template.py b/template/template.py index 2abc3f74..9cefbc80 100644 --- a/template/template.py +++ b/template/template.py @@ -40,13 +40,17 @@ def make_template( ) .run_cmd( r"sed -i -E '/^mozilla\/(emSign_ECC_Root_CA_-_G3|Comodo_AAA_Services_root)\.crt$/s/^/!/' /etc/ca-certificates.conf" + " && rm -f /etc/ssl/certs/emSign_ECC_Root_CA_-_G3.pem /etc/ssl/certs/Comodo_AAA_Services_root.pem" " && update-ca-certificates --fresh" ) - .run_cmd("python -m pip install --upgrade pip==26.2.2") + .run_cmd( + "python -m pip install --use-deprecated=legacy-certs" + " --cert /etc/ssl/certs/ca-certificates.crt --upgrade pip==26.2.2" + ) .run_cmd("curl -fsSL https://deb.nodesource.com/setup_20.x | bash -") .apt_install("nodejs") .run_cmd( - "echo 'export NODE_OPTIONS=--use-openssl-ca' > /etc/profile.d/node-ca.sh" + "echo 'export NODE_OPTIONS=\"--use-openssl-ca${NODE_OPTIONS:+ $NODE_OPTIONS}\"' > /etc/profile.d/node-ca.sh" ) .copy("requirements.txt", "requirements.txt") .pip_install("--no-cache-dir -r requirements.txt") @@ -98,7 +102,10 @@ def make_template( # Create server virtual environment .copy("server", ".server") .run_cmd("python -m venv .server/.venv") - .run_cmd(".server/.venv/bin/python -m pip install --upgrade pip==26.2.2") + .run_cmd( + ".server/.venv/bin/python -m pip install --use-deprecated=legacy-certs" + " --cert /etc/ssl/certs/ca-certificates.crt --upgrade pip==26.2.2" + ) # Copy and install server requirements .run_cmd( ".server/.venv/bin/pip install --no-cache-dir -r .server/requirements.txt" From 20d2473a3329a498ad0355d46baab1f89dbcb5dc Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 14:10:24 -0700 Subject: [PATCH 5/6] fix: remove blocked certificate copies on trust-store refresh --- template/remove-blocked-certs.sh | 13 +++++++++++++ template/template.py | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 template/remove-blocked-certs.sh diff --git a/template/remove-blocked-certs.sh b/template/remove-blocked-certs.sh new file mode 100644 index 00000000..e4bfb6e1 --- /dev/null +++ b/template/remove-blocked-certs.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -eu + +for certificate in /etc/ssl/certs/*; do + for blocked in \ + /usr/share/ca-certificates/mozilla/emSign_ECC_Root_CA_-_G3.crt \ + /usr/share/ca-certificates/mozilla/Comodo_AAA_Services_root.crt; do + if cmp -s "$certificate" "$blocked"; then + rm -f "$certificate" + break + fi + done +done diff --git a/template/template.py b/template/template.py index 9cefbc80..f5b4900c 100644 --- a/template/template.py +++ b/template/template.py @@ -38,9 +38,13 @@ def make_template( "ca-certificates", ] ) + .copy( + "remove-blocked-certs.sh", + "/etc/ca-certificates/update.d/remove-blocked-certs", + ) + .run_cmd("chmod +x /etc/ca-certificates/update.d/remove-blocked-certs") .run_cmd( r"sed -i -E '/^mozilla\/(emSign_ECC_Root_CA_-_G3|Comodo_AAA_Services_root)\.crt$/s/^/!/' /etc/ca-certificates.conf" - " && rm -f /etc/ssl/certs/emSign_ECC_Root_CA_-_G3.pem /etc/ssl/certs/Comodo_AAA_Services_root.pem" " && update-ca-certificates --fresh" ) .run_cmd( From 5925a040f9efe86e49c21afedcd3064317dfe428 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 20 Oct 2026 14:39:42 -0700 Subject: [PATCH 6/6] fix: use the updated Debian certificate package --- template/remove-blocked-certs.sh | 13 ------------- template/template.py | 11 +---------- tests/test_certificates.py | 8 ++++++++ 3 files changed, 9 insertions(+), 23 deletions(-) delete mode 100644 template/remove-blocked-certs.sh diff --git a/template/remove-blocked-certs.sh b/template/remove-blocked-certs.sh deleted file mode 100644 index e4bfb6e1..00000000 --- a/template/remove-blocked-certs.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -eu - -for certificate in /etc/ssl/certs/*; do - for blocked in \ - /usr/share/ca-certificates/mozilla/emSign_ECC_Root_CA_-_G3.crt \ - /usr/share/ca-certificates/mozilla/Comodo_AAA_Services_root.crt; do - if cmp -s "$certificate" "$blocked"; then - rm -f "$certificate" - break - fi - done -done diff --git a/template/template.py b/template/template.py index f5b4900c..c68541a5 100644 --- a/template/template.py +++ b/template/template.py @@ -35,18 +35,9 @@ def make_template( "jq", "sudo", "fonts-noto-cjk", - "ca-certificates", + "ca-certificates=20250419+deb13u1", ] ) - .copy( - "remove-blocked-certs.sh", - "/etc/ca-certificates/update.d/remove-blocked-certs", - ) - .run_cmd("chmod +x /etc/ca-certificates/update.d/remove-blocked-certs") - .run_cmd( - r"sed -i -E '/^mozilla\/(emSign_ECC_Root_CA_-_G3|Comodo_AAA_Services_root)\.crt$/s/^/!/' /etc/ca-certificates.conf" - " && update-ca-certificates --fresh" - ) .run_cmd( "python -m pip install --use-deprecated=legacy-certs" " --cert /etc/ssl/certs/ca-certificates.crt --upgrade pip==26.2.2" diff --git a/tests/test_certificates.py b/tests/test_certificates.py index 06484089..417f02bc 100644 --- a/tests/test_certificates.py +++ b/tests/test_certificates.py @@ -11,6 +11,7 @@ CHECK_CERTIFICATES = """ import ssl from importlib.metadata import version +from pathlib import Path import certifi from pip._vendor import certifi as pip_certifi @@ -21,6 +22,13 @@ blocked = {'emSign ECC Root CA - G3', 'AAA Certificate Services'} for bundle in (None, certifi.where(), pip_certifi.where()): context = ssl.create_default_context(cafile=bundle) + if bundle is None: + for path in Path('/etc/ssl/certs').iterdir(): + if path.is_file(): + try: + context.load_verify_locations(cafile=str(path)) + except ssl.SSLError: + pass certificates = context.get_ca_certs() assert certificates, f'Empty trust store: {bundle}' names = {