The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot

“Unable to load the service index for source https://api.nuget.org/v3/index.json.The SSL connection could not be established, see inner exception. The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot”

Docker cannot do a dotnet restore, because the certificate chain are broken

This was due to installing CISCO VPN Anyconnect-win Umbrella
Umbrella stops DNS hijacking something that is common in docker.
So make sure your IT department does not roll out this new safety feature without testing it with the devs first.
I had to roll back to anyconnect-win-4.9.00086-core-vpn and not connect (otherwise it would upgrade) to get Docker back to normal.

Follow up -
It looks like the issue here is TLS interception: something (Umbrella) is intercepting HTTPS requests allowing it to read (and modify) any web traffic. Applications will see this as a “man-in-the-middle” attack and close the connection. In order to work around this Umbrella needs to be added as a trusted root certification authority in all Docker containers that make Internet bound HTTPS requests.

To do this, first export the Umbrella CA by loading certlm.msc, expanding “Trusted Root Certification Authority”, identifying the CA then right-clicking, “All tasks”, “Export”, “Next”, “Base-64 encoded X.509 (.CER)”, “Next”, then save it somewhere in the Docker context so likely the root of your git repo. In the examples below I’ve assumed it’s named “umbrella.crt” but update as necessary.

For Linux containers add this to each Dockerfile:

ADD umbrella.crt /usr/local/share/ca-certificates/umbrella.crt

RUN chmod 644 /usr/local/share/ca-certificates/umbrella.crt && update-ca-certificates

For Windows containers:

ADD umbrella.crt C:\umbrella.crt

RUN Import-Certificate -FilePath C:\umbrella.cert -CertStoreLocation Cert:\LocalMachine\Root\

It will mean having to create custom images for any standard images, e.g. ones that are referenced directly from Docker Hub. For example command like ( docker run alpine/git ) still won’t work as you’ll have to create a Dockerfile for it to add the certificate, build it then run it. This is obviously not ideal.

TLS interception will break developer workflows in unexpected ways. Allow lists might work but there could be a lot of hosts to add, including wildcards, and will likely change over time. Is a transparent proxy possible instead? That would still be able to sniff certificate data so can pick out the host names without breaking TLS connections (although I think this was purposefully designed out in TLS 1.3 for privacy reasons).
If an allow list is the route to go then the obvious ones are possibly:

  • Docker Hub
  • Microsoft Container Registry
  • Google Container Registry
  • Nuget repositories
  • NPM

Follow up:
Having implemented egress filtering for environments before I’ve seen the unhelpful “CANCELLED” message in the first screenshot, and gone down a rabbit hole of packet tracing to work out exactly what the issue was. The second screenshot has a slightly better message saying it was unable to verify the issuer certificate, which means the process was able to establish a network connection and send/receive data however failed on the certificate check. Usually when that happens it’s because the CA isn’t trusted, could be because it’s an internal service or MITM is going on.
Conceptually Docker containers can be thought of as completely isolated processes running on the host – it has no access to the file system, and therefore the host’s list of CA s. Similar to how a VM won’t inherit the CAs from the hypervisor.

I should point out that the Linux Dockerfile commands will work for Debian based systems. If you’re using an Alpine base image (as we do) or Red Hat base image then the RUN command will be different but the premise is the same and hopefully should be easy to find. And that’s just a workaround until your IT/NOC add the exceptions to the allow list.

Hey @dougthompson ,

I am getting the same issue in dotnet restore
Would appreciate if you can list the exact steps to be followed in order to fix this issue.
I don’t have cisco installed, may be IT is blocking something.
Thanks