Skip to content

feat(computer): every runtime in a computer routing sites trusts the deploy's CA (FD-4933) - #537

Merged
earakely-scale merged 5 commits into
mainfrom
edgararakelyan/fd-4933-m54-the-deploys-ca-its-trust-step-and
Nov 8, 2026
Merged

earakely-scale merged 5 commits into
mainfrom
edgararakelyan/fd-4933-m54-the-deploys-ca-its-trust-step-and

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Nov 8, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

M5.4 (FD-4933): every program on a deployed Linux computer that checks TLS certificates trusts the deploy's CA, however it was started: browsers, the system store, Node, Python, programs reading SSL_CERT_FILE (Go, Ruby, OpenSSL tools) and Java. M5.7 (sites by name over HTTPS) and M5.17 (apps that depend on sites) need any app in the computer to load https://<site>/ without certificate errors. #448 shipped the CA itself and most stores; the outcome and four decisions are on FD-4933, decisions 1 and 2 revised once the local provider's trust files turned up. Site routing stays Linux-only here, shaped so macOS and Windows can be added: they're M6.6 (FD-5335).

  • The gap this closes: the trust variables lived only in /etc/profile.d, which only login shells read. Apps launched at boot or from the menu, and computer-exec commands not run through a login shell, never saw NODE_EXTRA_CA_CERTS or REQUESTS_CA_BUNDLE, and nothing set SSL_CERT_FILE.
  • One convention for the trust agentenv adds (decisions 1 and 2): the local Docker provider already copies its transfer CA into containers as /etc/agentenv/ca.pem and /etc/agentenv/ca-bundle.pem (the public roots plus it) and points the three variables at them. That's now the convention for the sites CA too: /etc/agentenv/ca.pem holds every CA agentenv adds and is the fixed path the design names for an app with a certificate list of its own; ca-bundle.pem holds the public roots plus them. A computer routing a site served over HTTPS boots with the same variables, so every process inherits them; one routing only .localhost names, for which no CA is made, boots without them. The files and variables are kept per OS type (sites.TRUST_ENVIRONMENTS), whose Linux entry is the provider's LOCAL_TRUST_ENV, so the two can't disagree. Routing appends the CA to both files; both are readable by everyone. The profile script now sets the same variables, sent from the same table, for shells that start a fresh login environment.
  • The files exist from boot: the ubuntu-24.04 and ubuntu-24.04-headless images ship an empty ca.pem and ca-bundle.pem as a copy of the system bundle. An app's services start at boot, before routing, and inherit the variables, so until routing appends the CA they get the public roots instead of a missing file. A local run's provider still copies its own files over them.
  • The bundle is made again at routing: routing makes ca-bundle.pem from the system's file of public roots as it is then, plus every CA in ca.pem (a provider's and the sites'). That file is Debian's (/etc/ssl/certs/ca-certificates.crt) or Red Hat's (/etc/pki/tls/certs/ca-bundle.crt), and update-ca-certificates has just added the sites CA to it. So the bundle also holds any CA a composed app added to the system store after the image was built. On a Linux with neither file, routing keeps the bundle the image or a provider made; with no bundle either, it's refused, naming both files, rather than leave a bundle of the CA alone, which would break public HTTPS for every program reading SSL_CERT_FILE.
  • Refused off Linux: site routing is refused at save and at deploy on a computer that isn't Linux. A macOS computer wasn't refused before, but the install only knows Linux and would have applied part of it; Windows computers were already refused through their disk form.
  • Java (decision 3): apt JDKs trust it through ca-certificates-java's hook, which update-ca-certificates runs; every other JDK under /usr/lib/jvm, /opt and the app folders gets it with keytool, its own else any. Each store is checked with keytool -list for the CA's SHA-256 fingerprint and recorded in trust.java.
  • Checks (decision 4): besides each site through Python's system store, the install loads the first HTTPS site through Python with SSL_CERT_FILE alone, and through Node and curl where the computer has them, as the session user. Each result is kept in the deploy record's sites.runtimes, and any runtime or JDK store that fails fails the deploy, naming it.
  • Universe-clock validity stays waiting on the clock work.

How it was tested

  • ruff check: clean.

  • Unit tests: test_computer_site_proxy.py: the install writes the shared trust files and the profile script, its bundle from Red Hat's public roots; the CA joins files a provider made, idempotently, or a bundle started from the system's; the bundle is made again from the system store, holding a CA an app added after the image was built and a provider's CA from ca.pem; an empty bundle with no public roots to start from is refused before either file is written; JDKs: an apt one through the hook store (listed only), a downloaded one and a bundled one without keytool (imported), a link to the same store once, a store whose password differs and one the CA isn't listed in (each named); the runtime checks, with SSL_CERT_FILE unset named. test_computer_sites.py: each failure named; Linux's trust variables equal the local provider's. test_computer_env.py: a routing computer boots with them, one without routing doesn't; the route is given the computer's OS type. test_computer_vm_bundle.py: site routing into a macOS computer is refused, at save and through the check the deploy runs. tst/unit/env and tst/unit/providers: 3929 passed; the one test_disk_recipe failure fails on macOS only.

  • VM e2e, agent-environments-admin, amd64 and arm64: a new test bundle, tls, whose clients app installs Node, an apt JDK and curl, copies the JDK under /opt with a store of its own, and, launched at boot, loads its URL through each. With a site routed into the computer, ca_trust_test.py checks that:

    • the app launched at boot loaded the site through Node, both JDKs and curl;
    • a command the computer runs sees the three variables and loads the site through Python with SSL_CERT_FILE alone;
    • /etc/agentenv/ca.pem holds the CA, mode 644, and /etc/agentenv/ca-bundle.pem holds it beside the public roots;
    • the record keeps each runtime's check and both JDK stores, trusted.

    The routed-site and start-tab tests from M5.12 run again beside it. On f47f064 all 5 passed on both arches (about 14 minutes each). The composed images carry the two files from build: an empty ca.pem and ca-bundle.pem with 120 public roots, both mode 644. After Greptile's round-3 finding (the image's copy would miss a CA an app adds), the bundle is made again at routing; on ece919c the CA trust test passed again on both arches.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no blocking issue remains.

Summary

The PR gives Linux computers shared CA files and trust settings for browsers, Python, Node, curl, and Java.

  • Linux computers pass the deploy’s CA trust to apps started in different ways.
  • Site routing now rejects computers that are not running Linux.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Shared Linux trust settings] --> B[Computer boot environment]
  A --> C[Fresh routing config]
  C --> D[Install CA in trust stores]
  C --> E[Write login shell exports]
  D --> F[Check sites and runtimes]
Loading

Reviews (8) · Last reviewed commit: "refactor(computer): the profile script s..." · Reviewed by Greptile

…deploy's CA, however it was started

The trust variables lived only in /etc/profile.d, which only login shells read, so apps launched at boot or from the
menu and computer-exec commands never saw them. The local provider's trust convention is now agentenv's for the sites
CA too: /etc/agentenv/ca.pem holds every CA agentenv adds and ca-bundle.pem the public roots plus them, a computer
routing sites boots with the same SSL_CERT_FILE, REQUESTS_CA_BUNDLE and NODE_EXTRA_CA_CERTS, and routing appends the
CA to both files. Every JDK trusts it, apt ones through the system store's hook and the rest by keytool, and routing
checks Python through SSL_CERT_FILE alone, Node and curl too, failing the deploy on any runtime or JDK store that
doesn't trust it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner November 8, 2026 07:02
earakely-scale and others added 2 commits November 7, 2026 23:22
… the trust variables

With every routed name a .localhost one, no CA is made and the install writes no trust files, so SSL_CERT_FILE and
REQUESTS_CA_BUNDLE named files that didn't exist and every public HTTPS request through them failed. The computer now
boots with them only when a site it routes is served over HTTPS, as issue() decides a CA is needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the trust files in the Ubuntu bases, and is refused off Linux

- The bundle of public roots plus the CAs agentenv adds starts from Debian's or Red Hat's file of public roots where
  neither the image nor a provider made it, and routing is refused on a Linux with neither rather than leave it the
  CA alone.
- The ubuntu-24.04 and ubuntu-24.04-headless images ship /etc/agentenv/ca.pem (empty) and ca-bundle.pem (the system
  bundle), so services started at boot, before routing, find the files the trust variables name.
- The trust files and variables are kept per OS type (sites.TRUST_ENVIRONMENTS, Linux's the local provider's), and
  site routing is refused at save and at deploy on a computer that isn't Linux, which a macOS computer would have
  half-applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale and others added 2 commits November 8, 2026 05:38
…tore, so it holds CAs apps added after the image was built

The Ubuntu bases copy the system bundle into /etc/agentenv/ca-bundle.pem when they're built, before a composed app's
install runs. An app that added a CA with update-ca-certificates would have been left out of the bundle routing kept,
so clients reading SSL_CERT_FILE rejected it. Routing now makes the bundle from the system store's public roots as they
are then, plus every CA in ca.pem; a Linux with no file of public roots keeps the bundle it has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…per-OS table names, with no fallback

The host now sends the OS's trust variables with the trust files, and install writes exactly those to the profile
script, so login shells and the boot environment can't disagree when a variable is added. The fallback for a config
without trust files, which nothing sends since the proxy is copied with the host on every route, is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale merged commit fa87148 into main Nov 8, 2026
34 of 35 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/fd-4933-m54-the-deploys-ca-its-trust-step-and branch November 8, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant