Engineering notes · October 2026

Patching Ubuntu, Rust and Go After the eMudhra Certificate Incident

Your browser may reject a compromised certificate while your backend still accepts it. Check the operating system, application dependencies and deployed binaries.

One Server Can Have Several Trust Stores

The October 2026 eMudhra incident exposed compromised intermediate certificate authority keys. Clients accepting the affected chains could trust an impersonated website or service.

Browser vendors shipped protections, but your browser, curl, Rust services and Go binaries do not necessarily validate certificates in the same way.

Updating Ubuntu is a good start. It does not replace certificate verification code already compiled into an application.

These instructions reflect the advisories available on 22 October 2026. The listed versions are incident-specific minimums; use newer supported security releases where available.


Start with Ubuntu

Apply the normal system updates:

sudo apt update
sudo apt upgrade

For a targeted update, Canonical provides:

sudo apt update
sudo apt install --only-upgrade ca-certificates 'libnss3*'

Check what is installed and what your repositories offer:

apt-cache policy ca-certificates libnss3

The fixed ca-certificates versions for these Ubuntu releases are:

Check the NSS advisory for your release's libnss3 version. Older Ubuntu releases need the applicable Ubuntu Pro coverage.

If you use Snap-installed browsers, refresh those separately:

sudo snap refresh

Restart affected applications afterward. Java users should also follow Canonical's additional trust-store instructions; its advisory identifies a synchronization issue requiring manual action.


Rust: Check What You Actually Compiled

A Rust application may use system certificates, bundled roots or a custom configuration. Start by inspecting its dependency graph:

cargo tree --locked | rg 'rustls-webpki|webpki-roots|webpki-root-certs|native-tls|openssl'

If you do not have rg, inspect cargo tree --locked directly.

The relevant RustSec fixes include:

Update the packages present in your project. With one version of each package in the graph, the commands are:

cargo update -p rustls-webpki
cargo update -p webpki-roots
cargo update -p webpki-root-certs

Only run the relevant commands. If Cargo reports multiple matching versions, qualify the package with its installed version. If dependency constraints prevent the fix, upgrade the parent dependency.

Check compatibility wrappers too. A 0.26.11 roots wrapper must resolve its underlying 1.x dependency to 1.0.10 or newer.

Review the changes, test and rebuild:

git diff -- Cargo.toml Cargo.lock
cargo test --locked
cargo build --release --locked

Deploy the new executable and restart the service. Running rustup update alone does not update the dependencies in your application.

Read the verification-library advisory and the bundled-roots advisory for affected versions and release-line details.


Go: Rebuild with the Patched Standard Library

Go's fix changes crypto/x509 to reject chains containing the affected intermediates. It is included in Go 1.27.3 and Go 1.26.10.

Check your toolchain and the binary you actually deployed:

go version
go env GOTOOLCHAIN
go version -m /path/to/deployed-service

Upgrade through your normal installation process. If your Go installation supports automatic toolchain downloads, you can explicitly select a patched version:

GOTOOLCHAIN=go1.27.3 go version
GOTOOLCHAIN=go1.27.3 go test ./...
GOTOOLCHAIN=go1.27.3 go build -o ./bin/service ./cmd/service
go version -m ./bin/service

Replace ./cmd/service with your main package. Use a newer supported patched release if your project requires one; do not downgrade to match this example.

Update CI and build images too. Installing a new compiler does not change an old executable, and updating module dependencies does not replace the standard library compiled into it.

References: Go's security fix, release history and toolchain selection.


Do Not Forget the Containers

Updating the host does not update certificate files inside existing container images. Multi-stage builds may need changes to both the builder and runtime stages.

After updating the Dockerfile's toolchain and base-image references:

docker compose build --pull --no-cache
docker compose up -d

Confirm that the running containers contain the new binary and trust configuration. Check mounted CA bundles and custom certificate pools as well.


Verify the Result

The incident disclosure provides test endpoints for affected certificate chains:

curl -v --connect-timeout 10 --max-time 20 \
  https://dv-g3b-emudhra.we-have-a-problem.com/

curl -v --connect-timeout 10 --max-time 20 \
  https://dv-g3b-aaa-emudhra.we-have-a-problem.com/

You want a certificate-verification failure. A timeout or DNS failure does not prove that certificate validation rejected the chain. Keep verification enabled; do not add -k.

Curl only tests its own TLS configuration. Repeat the checks through your actual Rust or Go client's request path, inside the deployed environment. If a proxy replaces server certificates, also test a path that exercises the original certificate chain.

Test ordinary production connections afterward too. If a legitimate service still presents an affected chain, correct its certificate configuration rather than disabling verification.

The incident disclosure includes additional test chains and offline verification instructions.


Patch the Software That Is Running

The useful question is not just “did we install the update?” It is “which certificate verifier is this deployed process using?”

Update the packages, rebuild affected applications, redeploy them and verify the result. That is what gets the fix from a security advisory into production.

We work with Rust, Go and Ubuntu infrastructure, including dependency maintenance and deployment systems. Reach out if your stack needs a hand.

Happy Hacking!