Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions template/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ urllib3==2.7.0
xarray==2025.4.0
xlrd==2.0.2
sympy==1.14.0
certifi==2026.10.19
1 change: 1 addition & 0 deletions template/server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ websockets==12.0
uvicorn[standard]==0.30.1
requests==2.33.0
pydantic==2.9.1
certifi==2026.10.19
2 changes: 2 additions & 0 deletions template/start-up.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/bash

export NODE_OPTIONS="--use-openssl-ca${NODE_OPTIONS:+ $NODE_OPTIONS}"

install -d -m 0700 /run/e2b-jupyter

function start_code_interpreter() {
Expand Down
1 change: 1 addition & 0 deletions template/systemd/jupyter.service
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion template/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -34,11 +35,18 @@ def make_template(
"jq",
"sudo",
"fonts-noto-cjk",
"ca-certificates",
"ca-certificates=20250419+deb13u1",
]
)
.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${NODE_OPTIONS:+ $NODE_OPTIONS}\"' > /etc/profile.d/node-ca.sh"
)
.copy("requirements.txt", "requirements.txt")
.pip_install("--no-cache-dir -r requirements.txt")
)
Comment thread
matthewlouisbrockman marked this conversation as resolved.
Expand Down Expand Up @@ -74,6 +82,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",
Expand All @@ -88,6 +97,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 --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"
Expand Down
106 changes: 106 additions & 0 deletions tests/test_certificates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import json
import shlex

import pytest
from e2b import Sandbox

from harness import CodeInterpreter

pytestmark = pytest.mark.skip_debug

CHECK_CERTIFICATES = """
import ssl
from importlib.metadata import version
from pathlib import Path

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)
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 = {
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


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",
]


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
Loading