Zscaler SSL/TLS Inspection terminates every HTTPS connection and re-signs it with the organization's root CA. Zscaler Client Connector installs that root into the Windows trust store, so Edge, PowerShell and everything else on the host is happy. A WSL distro or a Hyper-V Linux guest is a separate operating system with its own CA bundle, and nothing propagates into it. So apt-get update, curl, wget and git clone all fail with certificate verification errors on a machine that looks perfectly fine from Windows.

The fix on any Debian or Ubuntu system is three commands. The traps are all in the details: the file extension, the encoding and the file mode.

This note covers the native system trust store on Linux, which is what the OS and everything reading OpenSSL's default paths use. You need the certificate file first: see Getting the Zscaler root CA certificate out of Zscaler.

1. Get the certificate into the guest

You need the root CA in PEM form, commonly named ZscalerRootCertificate-2048-SHA256.crt. Obtaining it (and checking you have the right CA, since orgs using a custom intermediate need their own root instead) is covered in Getting the Zscaler root CA certificate out of Zscaler.

WSL can read the Windows filesystem directly, so no transfer is needed:

cp /mnt/c/Users/<you>/Downloads/ZscalerRootCertificate-2048-SHA256.crt ~/

Hyper-V guests and remote Linux hosts have no /mnt/c. Use scp, or paste it in over an existing console session, which avoids needing working TLS to fix broken TLS:

cat > ~/zscaler.crt <<'EOF'
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIJANu+mC2Jt3uN...
-----END CERTIFICATE-----
EOF

Confirm it is PEM and not DER before going further. PEM is text and starts with the BEGIN line; DER is binary and openssl will tell you so:

head -1 ~/zscaler.crt                          # -----BEGIN CERTIFICATE-----
openssl x509 -in ~/zscaler.crt -noout -subject -issuer -enddate

If openssl x509 errors out, it is DER. Convert it:

openssl x509 -inform der -in zscaler.der -out zscaler.crt

2. Install it into the system trust store

update-ca-certificates ships in the ca-certificates package, which minimal images and container base images frequently do not have. Install it first, otherwise the command simply is not found:

sudo apt-get update
sudo apt-get install -y --no-install-recommends ca-certificates

--no-install-recommends keeps this from dragging in openssl and friends on a slim image. On a normal desktop WSL distro it makes no practical difference.

Then place the cert and rebuild the bundle:

sudo cp ~/zscaler.crt /usr/local/share/ca-certificates/zscaler.crt
sudo chmod 644 /usr/local/share/ca-certificates/zscaler.crt
sudo update-ca-certificates

Expected output, and the thing to actually look at:

Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.

1 added is the proof. If it says 0 added, the cert was not picked up and one of the traps below applies. update-ca-certificates appends every anchor in /usr/local/share/ca-certificates/ to /etc/ssl/certs/ca-certificates.crt and drops a symlink into /etc/ssl/certs/, which is what OpenSSL, GnuTLS, curl, wget and the whole apt stack read.

The three traps

The file must end in .crt. update-ca-certificates only picks up files matching *.crt in that directory. A cert saved as zscaler.pem or ZscalerRootCertificate-2048-SHA256.cer is silently ignored, and you get 0 added with no error explaining why. Rename it; the contents are still PEM regardless of what the extension implies.

It must be PEM, despite the .crt extension. .crt says nothing about encoding. A DER file named .crt gets copied into the bundle as binary garbage and quietly breaks verification for every tool reading it, which is a worse outcome than being ignored.

Mode 644. Zscaler's own documentation lists sudo chmod 644 <root certificate file path> in its Ubuntu/Debian block (in the slot where sudo update-ca-certificates belongs, which is a documentation bug worth knowing about if you follow that page literally). The chmod matters on its own: a cert copied in as 600 or owned oddly is unreadable by non-root processes, so sudo curl works while curl fails, which is a confusing way to spend an afternoon.

Other distro families

Same idea, different directory and command. Both the root and any intermediate CA go in as individual PEM files.

Distro family Anchor directory Rebuild command
Debian, Ubuntu /usr/local/share/ca-certificates/ (.crt only) sudo update-ca-certificates
RHEL, Fedora, CentOS, Rocky, Alma /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust
OpenSUSE, SLES /etc/pki/trust/anchors/ sudo update-ca-certificates
Alpine /usr/local/share/ca-certificates/ sudo update-ca-certificates

3. Verify

Use the plaintext check endpoint on this site rather than a random third party. It returns an "IT WORKED" banner plus the request as the server saw it, so a 200 over a verified certificate is the proof:

curl https://www.cmunroe.us/check          # human-readable banner + client details
curl https://www.cmunroe.us/check?json     # machine-readable JSON, for scripting
wget -qO- https://www.cmunroe.us/check     # same, via wget

A banner instead of a TLS error means the trust store is correct and the proxy path works. To confirm you are genuinely being inspected (and therefore that it was the Zscaler root that fixed it, not a coincidence), look at who signed the certificate you received:

openssl s_client -connect www.cmunroe.us:443 -servername www.cmunroe.us </dev/null 2>/dev/null \
  | openssl x509 -noout -issuer

Behind Zscaler the issuer is a Zscaler CA rather than the real upstream issuer, and openssl s_client reports Verify return code: 0 (ok). Then confirm the package manager, which is usually the whole reason you are here:

sudo apt-get update

WSL specifics

  • Every distro is separate. wsl --list --verbose shows what you have; Ubuntu, Debian and a Docker Desktop distro each need their own copy. Fixing one does nothing for the others.
  • It survives reboots and wsl --shutdown, because it is a real file in the distro's filesystem. No re-run needed on login.
  • It does not survive wsl --unregister or a distro reset, which wipes the filesystem. Keep the cert somewhere on the Windows side (or in a provisioning script) so re-doing it is one command.
  • Corporate MDM does not reach into WSL. However the cert arrived on the Windows host, the Linux side is yours to maintain.