.. title: Docker behind SSL intercepting proxy
.. slug: docker-behind-ssl-proxy
.. date: 2018-06-08
.. tags: docker
.. category: 
.. link: 
.. description: 
.. type: text

## Situation 
* You need to build a Docker image from a repository in the Internet.
* Your application running in the Docker container accesses an HTTPS server in the Internet.
* You are behind intercepting SSL proxy.

The examples below are for Debian-based systems unless noted otherwise.

## Background

SSL intercepting proxy requires that the application:

1. Sends HTTPS requests to the proxy (as opposed to the target host directly)
2. Trusts the intercepting certificate.

These two actions are configured separately:

* For the former, one usually sets the environment variable `https_proxy`.
HTTPS clients which read the environment are supposed to use it; others require
own specific configuration.
* For the latter, the trust can be configured
either "system-wide", or on the application basis. Being behind the intercepting
proxy you might want the system-wide configuration. 

## Pulling images

Images are pulled by the Docker daemon.
The daemon is (normally) not aware of the environment of the shell you are using,
so the `$http_proxy` in your environment, if set, will not have any effect. 

```
$ docker pull hello-world
Using default tag: latest
Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io on 127.0.1.1:53: no such host
```

We need to configure the daemon to use the proxy according to the
[manual](https://docs.docker.com/config/daemon/systemd/#httphttps-proxy)
in `/etc/systemd/system/docker.service.d/http-proxy.conf`: 

```
[Service]
Environment="HTTPS_PROXY=https://proxy.example.com:443/" "NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"
```

And restart the daemon:

```
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
$ systemctl show --property=Environment docker
```

Docker daemon uses the proxy, but does not yet trust the intercepting certificate:

```
$ docker pull hello-world
[...]
docker: error pulling image configuration: Get https://[...]: x509: certificate signed by unknown authority.
```

You need to obtain your intercepting proxy certificate and add it to the system storage.
(Q: How to add it to docker only, without root access?):

```
$ sudo cp my-intercepting-cert.crt /usr/local/share/ca-certificates/
$ sudo update-ca-certificates 
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

done.
done.
```

Or on RedHat-based systems:
```
sudo cp my-intercepting-cert.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust extract
```

Now do `sudo systemctl restart docker` and you should be able to pull images.

## Running containers

The steps above affected the **daemon** only. If your **container** needs
to access the Internet
behind the proxy, you want your dockerized application to be aware of it.
While the build is run by the docker daemon, the daemon is not instructed to make
"proxy-aware" images; in the end, you can build non-proxified images if you need to.

[Docker documentation](https://docs.docker.com/network/proxy/) describes
three ways to pass environment variables to the image.

* In `Dockerfile`:

```
ENV http_proxy proxy.example.com:80
ENV https_proxy proxy.example.com:443
```

* In the build command line:

```
docker build -t my-proxified-image \
            --build-arg http_proxy="http://proxy.example.com:80" \
            --build-arg https_proxy="https://proxy.example.com:443" \
            .
```

* Configuring the Docker **client** (since Docker 17.07):

Add the snip to `~/.docker/config.json` in the user's home directory:
```
{
 "proxies":
 {
   "default":
   {
     "httpsProxy": "http://127.0.0.1:80",
     "noProxy": "*.test.example.com,.example2.com"
   }
 }
}

```

Your image can contain programs which require proxy to operate, but obtain it from
somewhere else than the environment.
Configuration for such programs must be provided on the individual basis.

* Example: if you want to be able to install software with "apt" in your Debian-based image:

```
RUN echo \
  'Acquire::http::proxy "http://proxy.example.com:80/";\n\
   Acquire::https::proxy "https://proxy.example.com:443/";'\
   > /etc/apt/apt.conf.d/00proxy.conf
```

**Note:** the application in the container needs to be able to resolve the
given proxy domain name.
A restrictive environment behind SSL proxy may not provide you with a 
general DNS access, in which case you'd have to use the IP address of the proxy.


Again, for SSL interception, in addition to the proxy itself the intercepting
certificate must be configured as trusted. A snippet from `Dockerfile` for a
"system-wide" configuration in a Debian-based system:

```
ADD dir-containing-intercepting-cert /usr/local/share/ca-certificates
RUN update-ca-certificates
```

### At the runtime

The environment variable can be passed individually per container:

```
$ docker run -e "https_proxy=http://proxy.example.com:80" my-image
```

## Afterword

SSL intercepting proxies [**reduce** the connection security in many cases](https://zakird.com/papers/https_interception.pdf).
Additionally to that, an application which performs certificate check but is not
configured to trust the intercepting certificate will show a security warning (or just silently fail);
as not every user is capable of properly configuring the trust in every case - 
in the end, users just need the application to work - 
many will ignore the warning and/or "set the insecure mode" if possible.
In this way, the SSL intercepting proxy
*teaches to ignore security warnings*, exactly rendering users vulnerable to attacks.

## Acknowledgements

* [crondev](https://crondev.com/running-docker-behind-proxy/)
