- Configuring CNTLM
- Configuring Docker for downloading images
- Adding Proxy variables to the GitLab Runner configuration
- Adding the Proxy to the Docker containers
- Proxy settings when using dind service
- Handling rate limited requests
Running GitLab Runner behind a proxy
This guide aims specifically to making GitLab Runner with Docker executor work behind a proxy.
Before continuing, ensure that you’ve already installed Docker and GitLab Runner on the same machine.
Configuring CNTLM
CNTLM is a Linux proxy which can be used as a local proxy and has 2 major advantages compared to adding the proxy details everywhere manually:
- One single source where you need to change your credentials
- The credentials can not be accessed from the Docker runners
Assuming you have installed CNTLM, you need to first configure it.
Make CNTLM listen to the docker0
interface
For extra security, and to protect your server from the outside world, you can
bind CNTLM to listen on the docker0
interface which has an IP that is reachable
from inside the containers. If you tell CNTLM on the Docker host to bind only
to this address, Docker containers are be able to reach it, but the outside
world can’t.
-
Find the IP that Docker is using:
ip -4 -oneline addr show dev docker0
This is usually
172.17.0.1
, let’s call itdocker0_interface_ip
. -
Open the configuration file for CNTLM (
/etc/cntlm.conf
). Enter your username, password, domain and proxy hosts, and configure theListen
IP address which you found from the previous step. It should look like this:Username testuser Domain corp-uk Password password Proxy 10.0.0.41:8080 Proxy 10.0.0.42:8080 Listen 172.17.0.1:3128 # Change to your docker0 interface IP
-
Save the changes and restart its service:
sudo systemctl restart cntlm
Configuring Docker for downloading images
Follow Docker’s documentation how to use a proxy.
The service file should look like this:
[Service]
Environment="HTTP_PROXY=http://docker0_interface_ip:3128/"
Environment="HTTPS_PROXY=http://docker0_interface_ip:3128/"
Adding Proxy variables to the GitLab Runner configuration
The proxy variables need to also be added to the GitLab Runner configuration, so that it can get builds assigned from GitLab behind the proxy.
This is basically the same as adding the proxy to the Docker service above:
-
Create a systemd drop-in directory for the
gitlab-runner
service:mkdir /etc/systemd/system/gitlab-runner.service.d
-
Create a file called
/etc/systemd/system/gitlab-runner.service.d/http-proxy.conf
that adds theHTTP_PROXY
environment variable(s):[Service] Environment="HTTP_PROXY=http://docker0_interface_ip:3128/" Environment="HTTPS_PROXY=http://docker0_interface_ip:3128/"
-
Save the file and flush changes:
systemctl daemon-reload
-
Restart GitLab Runner:
sudo systemctl restart gitlab-runner
-
Verify that the configuration has been loaded:
systemctl show --property=Environment gitlab-runner
You should see:
Environment=HTTP_PROXY=http://docker0_interface_ip:3128/ HTTPS_PROXY=http://docker0_interface_ip:3128/
Adding the Proxy to the Docker containers
After you register your runner, you may want to
propagate your proxy settings to the Docker containers (for example, for git clone
).
To do this, you need to edit /etc/gitlab-runner/config.toml
and add the
following to the [[runners]]
section:
pre_get_sources_script = "git config --global http.proxy $HTTP_PROXY; git config --global https.proxy $HTTPS_PROXY"
environment = ["https_proxy=http://docker0_interface_ip:3128", "http_proxy=http://docker0_interface_ip:3128", "HTTPS_PROXY=docker0_interface_ip:3128", "HTTP_PROXY=docker0_interface_ip:3128"]
Where docker0_interface_ip
is the IP address of the docker0
interface.
HTTP_PROXY
and others http_proxy
.
Unfortunately, there is no
standard
on these kinds of environment variables.Proxy settings when using dind service
When using the Docker-in-Docker executor (dind),
it may be necessary to specify docker:2375,docker:2376
in the NO_PROXY
environment variable. The ports are required, otherwise docker push
is blocked.
Communication between dockerd
from dind and the local docker
client (as described here: https://hub.docker.com/_/docker/)
uses proxy variables held in root’s Docker configuration.
To configure this, you need to edit /root/.docker/config.json
to include your complete proxy configuration, for example:
{
"proxies": {
"default": {
"httpProxy": "http://proxy:8080",
"httpsProxy": "http://proxy:8080",
"noProxy": "docker:2375,docker:2376"
}
}
}
In order to pass on the settings to the container of the Docker executor, a $HOME/.docker/config.json
also needs to be created inside the container. This may be scripted as a before_script
in the .gitlab-ci.yml
, for example:
before_script:
- mkdir -p $HOME/.docker/
- 'echo "{ \"proxies\": { \"default\": { \"httpProxy\": \"$HTTP_PROXY\", \"httpsProxy\": \"$HTTPS_PROXY\", \"noProxy\": \"$NO_PROXY\" } } }" > $HOME/.docker/config.json'
Or alternatively, in the configuration of the gitlab-runner
(/etc/gitlab-runner/config.toml
) that is affected:
[[runners]]
pre_build_script = "mkdir -p $HOME/.docker/ && echo \"{ \\\"proxies\\\": { \\\"default\\\": { \\\"httpProxy\\\": \\\"$HTTP_PROXY\\\", \\\"httpsProxy\\\": \\\"$HTTPS_PROXY\\\", \\\"noProxy\\\": \\\"$NO_PROXY\\\" } } }\" > $HOME/.docker/config.json"
"
is needed here because this is the creation of a
JSON file with a shell specified as a single string inside a TOML file.
Because this is not YAML, do not escape the :
.Note that if the NO_PROXY
list needs to be extended, wildcards *
only work for suffixes,
but not for prefixes or CIDR notation.
For more information, see
https://github.com/moby/moby/issues/9145
and
https://unix.stackexchange.com/questions/23452/set-a-network-range-in-the-no-proxy-environment-variable.
Handling rate limited requests
A GitLab instance may be behind a reverse proxy that has rate-limiting on API requests to prevent abuse. GitLab Runner sends multiple requests to the API and could go over these rate limits.
As a result, GitLab Runner handles rate limited scenarios with the following logic:
- A response code of 429 - TooManyRequests is received.
- The response headers are checked for a
RateLimit-ResetTime
header. TheRateLimit-ResetTime
header should have a value which is a valid HTTP Date (RFC1123), likeWed, 21 Oct 2015 07:28:00 GMT
.- If the header is present and has a valid value the runner waits until the specified time and issues another request.
- If the header is present, but isn’t a valid date, a fallback of 1 minute is used.
- If the header is not present, no additional actions are taken, the response error is returned.
- The process above is repeated 5 times, then a
gave up due to rate limit
error is returned.
RateLimit-ResetTime
is case insensitive since all header keys are run
through the http.CanonicalHeaderKey
function.