Search code examples
dockerhttp-proxydocker-toolboxhttps-proxy-agent

Configure Docker with proxy per host/url


I use Docker Toolbox on Windows 7 in a corporate environment. My workflow requires pulling containers from one artifactory and pushing them to a different one (eg. external and internal). Each artifactory requires a different proxy to access it. Is there a way to configure Docker daemon to select proxy based on a URL? Or, if not, what else can I do to make this work?


Solution

  • Since, as Pierre B. mentioned, Docker daemon does not support URL-based proxy selection, the solution is to point it to a local proxy configured to select the proper upstream proxy based on the URL.

    While any HTTP[S] proxy capable of upstream selection would do, (pac4cli project being particularly interesting for it's advertised capability to select the upstream based on proxy-auto-discovery protocol used by most Web browsers a in corporate setting), I've chosen to use tinyproxy, as more mature and light-weight solution. Furthermore, I've decided to run my proxy inside the docker-machine VM in order to simplify it's deployment and make sure the proxy is always running when the Docker daemon needs it.

    Below are the steps I used to set up my system. I'm especially grateful to phoenix for providing steps to set up Docker Toolbox on Windows behind a corporate proxy, and will borrow heavily from that answer.

    From this point on I will assume either Docker Quickstart Terminal or GitBash, with docker in the PATH, as your command line console and that "username" is your Windows user name.

    Step 1: Build tinyproxy on your target platform

    Begin by pulling a clean Linux distribution, I used CentOS, and run bash inside it:

    docker run -it --name=centos centos bash
    

    Next, install the tools we'll need:

    yum install -y make gcc
    

    After that we pull the latest release of Tinyproxy from it's GitHub repository and extract it inside root's home directory (at the time of this writing the latest release was 1.10.0):

    cd
    curl -L https://github.com/tinyproxy/tinyproxy/releases/download/1.10.0/tinyproxy-1.10.0.tar.gz \
        | tar -xz
    cd tinyproxy-1.10.0
    

    Now let's configure and build it:

    ./configure --enable-upstream \
        --disable-filter\
        --disable-reverse\
        --disable-transparent\
        --disable-xtinyproxy
    make
    

    While --enable-upstream is obviously required, disabling other default features is optional but a good practice. To make sure it actually works run:

    ./src/tinyproxy -h
    

    You should see something like:

    Usage: tinyproxy [options]
    
    Options are:
      -d        Do not daemonize (run in foreground).
      -c FILE   Use an alternate configuration file.
      -h        Display this usage information.
      -v        Display version information.
    
    Features compiled in:
        Upstream proxy support
    
    For support and bug reporting instructions, please visit
    <https://tinyproxy.github.io/>.
    

    We exit the container by pressing Ctrl+D and copy the executable to a special folder location accessible from the docker-machine VM:

    docker cp centos://root/tinyproxy-1.10.0/src/tinyproxy \
        /c/Users/username/tinyproxy
    

    Substitute "username" with your Windows user name. Please note that double slash — // before "root" is required to disable MINGW path conversion.

    Now we can delete the container:

    docker rm centos
    

    Step 2: Point docker daemon to a local proxy port

    Choose a TCP port number to run the proxy on. This can be any port that is not in use on the docker-machine VM. I will use number 8618 in this example.

    First, let's delete the existing default Docker VM:
    WARNING: This will permanently erase all currently stored containers and images

    docker-machine rm -f default
    

    Next, we re-create the default machine setting HTTP_PROXY and HTTPS_PROXY environment variables to the local host and the port we selected, and then refresh our shell environment:

    docker-machine create default \
        --engine-env HTTP_PROXY=http://localhost:8618 \
        --engine-env HTTPS_PROXY=http://localhost:8618
    eval $(docker-machine env)
    

    Optionally, we could also set NO_PROXY environment variable to list hosts and/or wildcards (separated by ;) to which the daemon should connect directly, bypassing the proxy.

    Step 3: Set up tinyproxy inside docker-machine VM

    First, we will create two files in the /c/Users/username directory (this is where our tinyproxy binary should reside after Step 1 above) and then we'll copy them to the VM.

    The first file is tinyproxy.conf, the exact syntax is documented on the Tinyproxy website, but the example below should have all the settings need:

    # These settings can be customized to your liking,
    # the port though must be the same we used in Step 2
    
    listen 127.0.0.1
    port 8618
    
    user nobody
    group nogroup
    loglevel critical
    syslog on
    
    maxclients 50
    startservers 2
    minspareServers 2
    maxspareServers 5
    
    disableviaheader yes
    
    # Here is the actual proxy selection, rules apply from top
    # to bottom, and the last one is the default. More info on:
    # https://tinyproxy.github.io/
    
    upstream http proxy1.corp.example.com:80 ".foo.example.com"
    upstream http proxy2.corp.example.com:80 ".bar.example.com"
    upstream http proxy.corp.example.com:82
    

    In the example above:

    • http://proxy1.corp.example.com:80 will be used to connect to URLs that end with "foo.example.com", such as http://www.foo.example.com
    • http://proxy2.corp.example.com:80 will be used to connect to URLs that end with "bar.example.com", such as http://www.bar.example.com, and
    • http://proxy.corp.example.com:80 will be used to connect all other URLs

    It is also possible to match exact host names, IP addresses, subnets and hosts without domains.

    The second file is as the shell script that will launch the proxy, its name must be bootlocal.sh:

    #! /bin/sh
    
    # Terminate on error
    set -e
    
    # Switch to the script directory
    cd $(dirname $0)
    
    # Launch proxy server
    ./tinyproxy -c tinyproxy.conf
    

    Now, let's connect to the docker VM, get root, and switch to boot2docker directory:

    docker-machine ssh
    sudo -s
    cd /var/lib/boot2docker
    

    Next, we'll copy all three files over and a set their permissions:

    cp /c/Users/username/boot2docker/{tinyproxy{,.conf},bootlocal.sh} .
    chmod 755 tinyproxy bootlocal.sh
    chmod 644 tinyproxy.conf
    

    Exit VM session by pressing Ctrl+D twice and restart it:

    docker-machine restart default
    

    That's it! Now docker should be able pull and push images from different URLs automatically selecting the right proxy server.