I have been trying to pull docker images to my work machine for a long time now. It’s such a pain because our desktops do not have direct internet access. Instead we need to use a proxy that requires authentication.

In docker for windows you can easily set a proxy. The problem is, if you want to use a proxy with authentication you need to specify the proxy with your domain user and the password, like so:

http://USER:PASSWORD@our-corporate-proxy:8080

The user and password are set in clear text which I’m not so comfortable with. I didn’t investigate if it’s possible in windows, too, but when running docker on a linux machine, you can set the proxy via environment variables … in which I also don’t want to set my password in clear text.

CNTLM

I’m not aware of a way to get around the above mentioned problem without any additional tool. After a little online search I found the cntlm tool. In short it’s a proxy that handles the communication and authentication with an upstream proxy so your tools won’t have to.

To be able to authenticate to the upstream proxy you also need to specify the credentials. Other than in the proxy url you can at least specify an NTLM hash and not a clear text password.

Configuration

After installing cntlm (in my case via choco install cntlm) it can be configured with the cntlm.ini file in the installation directory. First we need to create the hash that should go to the configuration file. This can be done with the following command in a cmd or powershell.

cntlm.exe -H -u DOMAINUSER -d DOMAIN

This will prompt you for your password and will then print the NTLM hash you need for configuration.

PassLM      XXXXX
PassNT      XXXXX
PassNTLMv2  XXXXX

In my case I just needed the last one. So I copied that one and put it in the cntlm.ini configuration file at the appropriate location (where the samples are commented). Don’t forget to change the user and domain settings. Also make sure the Auth NTLMv2 is set. It will look something like this at the beginning of the file:

#
# Cntlm Authentication Proxy Configuration
#
# NOTE: all values are parsed literally, do NOT escape spaces,
# do not quote. Use 0600 perms if you use plaintext password.
#

Username	YOUR_USERNAME
Domain		YOUR_DOMAIN.com

# NOTE: Use plaintext password only at your own risk
# Use hashes instead. You can use a "cntlm -M" and "cntlm -H"
# command sequence to get the right config for your environment.
# See cntlm man page
# Example secure config shown below.
Auth            NTLMv2
PassNTLMv2      XXXXXXXXXX

Save the file and check if it works:

cntlm.exe -M http://google.com

If everything is ok it should print something like

Config profile  1/4... OK (HTTP code: 301)
----------------------------[ Profile  0 ]------
Auth            NTLMv2
PassNTLMv2      XXXXXXXXX
------------------------------------------------

Docker

If you start the cntlm windows service you can now use that proxy via localhost:3128. In docker for windows you can set it via the UI.

Setting proxy in docker UI
Setting proxy in docker UI

Unfortunately after setting this proxy and running e.g. docker search grafana I always received the following error.

Error response from daemon: Get https://index.docker.io/v1/search?q=grafana&n=25: proxyconnect tcp: dial tcp 10.0.75.1:3128: i/o timeout

Hm, strange. Why does docker try to use 10.0.75.1:3128 instead of the localhost that I actually specified in the settings? Well I think this is because when using docker for windows, the docker host is actually hosted in a virtual machine - which is running on localhost, but it’s not the same as localhost. It’s in its own network and has its own IP address. Which can also be seen when doing an ipconfig -all.

ipconfig showing docker NAT ethernet adapter with ip 10.0.75.1
ipconfig showing docker NAT ethernet adapter with ip 10.0.75.1

Troubleshooting

So it seems, that cntlm does not accept connections from other machines, which is a good thing from the security perspective. So the internet connection that uses your credentials can at least only be used from your own machine. But unfortunately docker can’t use it. The solution for this is to set the Gateway configuration in the cntlm.ini file to yes. This allows other machines to use the proxy.

# Enable to allow access from other computers
#
Gateway	yes

To improve security I used the windows firewall and restricted inbound connections to CNTLM (Port 3128) to specifically allow the IPs of the docker NAT (in my case 10.0.75.1-10.0.75.15). So that are the only IPs that can use the proxy. You can also specify allowed IPs in the cntlm.ini file but that didn’t have any effect when I tried that. I probably did something wrong there. But it works fine with the firewall rules. Additionally I stop cntlm when I’m not actively using it to further reduce the possibility of misuse of this proxy.

Finally working

After all those changes (and restarting the cntlm service) I can finally search and pull docker images from the docker registry. What a fight …

working docker search command
working docker search command