Installing GitLab on Your Own Server (Part 5) – Configure Docker Registry

You can run a docker registry either inside GitLab or use your own external docker registry together with GitLab where GitLab is only responsible to authenticate access to your registry.

I will describe in this article how to integrate an external registry with GitLab. The major problem that you encounter when choosing this setup is that there is no single description how to do this.

When installing an external docker registry and use GitLab for authentication and authorization then we need a RSA key pair where GitLab gets the private key and the registry needs the public key.

All the following commands are written with the assumption that they are executed in the folder /etc/gitlab/registry.

RSA Key and Certificate

First I create a key for GitLab

$ openssl genrsa 4096 | sudo tee registry.pem > /dev/null

This command line creates a 4096-bit RSA key and writes it to the file registry.pem. The tee command is there to be able to write the file as root even if the command itself is executed by an unprivileged user. The redirection to /dev/null is there to not print the key to stdout.

Next I create a CSR that is used to create the certificate.

$ openssl req -new -sha256 -key registry.pem -subj "/CN=gitlab-registry" | sudo tee registry.csr

Now I sign this CSR with my private key and get a certificate for use

$ sudo openssl x509 -in registry.csr -out registry.crt -req -signkey registry.pem -days 365

Now I have the certificate in the file registry.crt, which is used by my registry. On the other side, used by GitLab, I have the RSA key registry.pem.

Now I change the mode of the key file with

$ sudo chmod go-rwx registry.pem

Start Docker Registry

Next I start the docker registry with

$ docker run -d \
--restart=always \
--name registry \
-v /etc/gitlab/registry:/certs \
-p 5000:5000 \
-e REGISTRY_AUTH_TOKEN_REALM=https://git.example.net/jwt/auth \
-e REGISTRY_AUTH_TOKEN_ISSUER=omnibus-gitlab-issuer \
-e REGISTRY_AUTH_TOKEN_SERVICE=container_registry \
-e REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE=/certs/registry.crt \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry:latest

The lines have the following meanings:

  1. Run a docker container in the background (-d
  2. Restart the container always when it exits
  3. Give the container the name registry
  4. Mount the folder with the key and certificate to /certs in the container
  5. Expose the port 5000 of the container as port 5000 in the host
  6. The URL of the authentication service (GitLab in our case). My GitLab hostname with /jwt/auth appended
  7. The name of the token issuer (must be the same name as defined in the GitLab installation for the key gitlab_rails['registry_issuer'])
  8. The service name that is being authenticated (fixed value from GitLab)
  9. The name of the certificate to be used (because of the mount in line 4 this is the effectively file /etc/gitlab/registry/registry.crt on the host)
  10. Enable deleting of images in the registry
  11. The name and version of the image to start

I add a reverse proxy for this registry also so that it can be reached via HTTPS.

Configure NGINX Reverse Proxy

Now I configure NGINX as TLS termination proxy and reverse proxy (similar to the configuration in part 2) with the following configuration file

upstream docker-registry {
  server localhost:5000;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name registry.example.net;
    server_tokens off;

    ssl on;
    ssl_certificate /etc/ssl/cert/registry.example.net.pem;
    ssl_certificate_key /etc/ssl/private/registry.example.net.key;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_session_cache shared:SSL:50m;
    ssl_dhparam /etc/ssl/cert/registry.example.net.dhparam;
    ssl_prefer_server_ciphers on;

    # 1 week HSTS
    add_header Strict-Transport-Security "max-age=604800; includeSubDomains;";
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;

    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;

    location / {
        client_max_body_size 0;
        gzip off;

        proxy_read_timeout      900;
        proxy_connect_timeout   900;
        proxy_redirect          off;

        proxy_http_version 1.1;

        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-Ssl     on;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto   $scheme;
        proxy_pass          http://docker-registry;
    }

    location /v2/ {
        client_max_body_size 0;
        gzip off;

        proxy_read_timeout      900;
        proxy_connect_timeout   900;
        proxy_redirect          off;

        proxy_http_version 1.1;

        add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-Ssl     on;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto   $scheme;
        proxy_pass          http://docker-registry;
    }

}

In line 2 I connect to the exposed port of the running registry.

In line 8 the hostname of the registry is defined.

In line 12, 13 and 18 the certificate, key and the dhparams for this site are configured.

The content of line 60 is important to use the registry as this header is used by the docker client.

Configure GitLab

Next I configure GitLab to use this registry and also to provide a login service for the registry.

gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "registry.example.net"
# gitlab_rails['registry_port'] = "443"
gitlab_rails['registry_api_url'] = "http://localhost:5000"
gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
gitlab_rails['registry_key_path'] = "/etc/gitlab/registry/certificate.key"
registry['internal_key'] = "---BEGIN RSA PRIVATE KEY---\nMIIEpQIBAA\n"

With this configuration there are some things that you need to know.

In line 2 you have to set the name of your registry host (which is defined in NGINX).

Line 3 defines the port which is appended to the registry inside a CI job. Because I use the default port (HTTPS 443) I can leave this out.

In line 6 the issuer is defined which must match the issuer in the registry configuration.

Line 7 defines the file where the key is saved to. This file will be overwritten each time you reconfigure GitLab.

Line 8 contains the private key (/etc/gitlab/registry/registry.pem) in a single line. The line breaks in the file must be replaced with \n.

After changing the configuration file of GitLab don’t forget to reconfigure GitLab with

$ sudo gitlab-ctl reconfigure

Next I will show how to set up the pages in GitLab and use NGINX as TLS termination proxy for this.

Parts

Reference

3 thoughts on “Installing GitLab on Your Own Server (Part 5) – Configure Docker Registry

  1. Pingback: Installing GitLab on Your Own Server (Part 1) – Installation | Blog at sw4j.de

  2. Pingback: Installing GitLab on Your Own Server (Part 3) – Install gitlab-runner | Blog at sw4j.de

  3. Pingback: Installing GitLab on Your Own Server (Part 2) – Configure NGINX Reverse Proxy | Blog at sw4j.de

Leave a Reply

Your email address will not be published. Required fields are marked *