# GitLab

GitLab (opens new window) is a highly customizable, highly configurable tool to manage source code, project management, and many other aspects of project development. In addition to the SaaS product, its self-hosted solution and easy free-to-enterprise upgrade path make it a popular choice for those managing sensitive code bases.

This guide demonstrates how to configure a self-hosted GitLab server (a.k.a. gitlab-ee) behind Pomerium for identity-aware access.

# Before You Being

  • This guide is written for an environment using Docker Compose (opens new window).

  • This guide assumes a running instance of Pomerium, already configured with an identity Provider (IdP) and running as a docker container on the same host/swarm. See our Quick-Start page for more information on installing Pomerium with Docker Compose.

    WARNING

    While Pomerium can be configured to use self-hosted GitLab as an IdP, we do not recommend doing so while also running it behind Pomerium. In addition to the potential to lock out access to the IdP (breaking access to all routes), we consider it best practice to separate your identity provider and protected services, especially those housing sensitive data like source code.

  • The initial setup documented here uses un-encrypted HTTP traffic between Pomerium and GitLab. The last section covers upgrading the GitLab configuration with a TLS certificate.

# Install GitLab

TIP

While we do our best to keep our documentation up to date, changes to third-party systems are outside our control. Refer to GitLab Docker Images (opens new window) from GitLab's docs as needed, or let us know (opens new window) if we need to re-visit this section.

# Prepare The Environment

  1. Create volumes for persistent data. This guide uses the base directory /srv/gitlab; adjust this path for your local environment:

    mkdir -p /srv/gitlab #Adjust the path based on your common Docker volume location.
    
  2. Create three sub-directories: config, data, and logs:

    mkdir -p /srv/gitlab/{config,data,logs}
    

# Install and Configure GitLab

  1. Edit your docker-compose.yml file to define a new service for GitLab:

    services:
    
    ...
    
    gitlab:
      container_name: gitlab
      image: gitlab/gitlab-ee:latest
      environment:
        GITLAB_OMNIBUS_CONFIG: |
          external_url 'https://gitlab.pomerium.localhost.io'
          letsencrypt['enable'] = false
          nginx['listen_port'] = 80
          nginx['listen_https'] = false
      volumes:
        - '/srv/gitlab/config:/etc/gitlab'
        - '/srv/gitlab/logs:/var/log/gitlab'
        - '/srv/gitlab/data:/var/opt/gitlab'
      expose:
        - 80
        - 443
        - 22
      restart: always
      shm_size: '256m'
    
    • Adjust external_url and registry_external_url to match the external path, which we will define in Pomerium later in the process.
    • Adjust the paths under volumes to match the directories created in the previous section.

    TIP

    Additional integrations like Mattermost (opens new window) and Pages (opens new window) will require additional configuration (i.e. mattermost_nginx[*]).

  2. Bring up the new Docker Compose configuration:

    docker-compose up -d
    

    The container may take several minutes to initialize. Once complete, the status provided to docker ps will change from health:starting to healthy.

    You can also monitor the progress with docker logs -f gitlab. Note that even after the process is complete, the log file will continue to output log messages.

# Configure a Pomerium Route

Edit config.yaml and add a route for GitLab:

  - from: https://gitlab.localhost.pomerium.io
    to: http://gitlab
    preserve_host_header: true
    policy:
      - allow:
          or:
            - domain:
                is: example.com

Once the route is applied, you should be able to access GitLab from https://gitlab.localhost.pomerium.io. Note that when using Docker, you may need to restart the Pomerium container to apply the changes as file change detection can be finicky on mounted docker volumes.

Use grep within the container to find the default root password:

sudo docker exec -it gitlab grep 'Password:' /etc/gitlab/initial_root_password

# Configure TCP Connections

  1. An additional route will provide an encrypted TCP tunnel through which users can securely access code using Git:

      - from: tcp+https://gitlab.localhost.pomerium.io
        to: tcp://gitlab:22
        policy:
          - allow:
              or:
                - groups:
                    has: devs@example.com
    
  2. Once this route is applied, users can create an encrypted connection using pomerium-cli or the Pomerium Desktop (opens new window) app:

  3. Add the tunneled connection as a remote:

    git remote add gitlab-tunneled ssh://git@127.0.0.1:2202/username/project-name
    

Now when you first initiate a pull, push, or fetch command your web browser will open to authenticate and authorize the connection.

# Identity Management

While GitLab has a JWT OmniAuth provider (opens new window), it only accepts a JSON web token (JWT) as a callback url parameter and not as an HTTP header provided by Pomerium. If your IdP is a supported OmniAuth provider (opens new window), you can integrate it directly to GitLab to re-use your current session, but it will require signing in twice; once with Pomerium and again with GitLab:

# Upstream TLS

As part of a complete zero trust security model, all connections should be encrypted and mutually authenticated. An important step in this process is configuring GitLab to serve content to Pomerium using HTTPS.

Because GitLab's internal Nginx server is configured to use the FQDN (opens new window) even when behind a proxy service, GitLab itself must use a separate, internal certificate for gitlab.pomerium.localhost.io. This is unique from the certificate Pomerium uses to serve the route to end users.

Create this certificate using your infrastructure's preferred internal certificate solution. If you don't have a solution in place, you can use mkcert (opens new window) to generate testing certificates:

mkcert

After installing mkcert (opens new window), confirm the presence and names of your local CA files:

mkcert -install
The local CA is already installed in the system trust store! πŸ‘
The local CA is already installed in the Firefox and/or Chrome/Chromium trust store! πŸ‘

ls "$(mkcert -CAROOT)"
rootCA-key.pem  rootCA.pem

The output of mkcert -install may vary depending on your operating system.

  1. Create a certificate for the domain that will be used for the GitLab route. For example:

    mkcert "gitlab.pomerium.localhost.io"
    
  2. Note the location of the mkcert root certificate files with mkcert -CAROOT. You will need to provide this path to your Pomerium configuration to validate the certificate provided by GitLab.

If you have an internal certificate solution, generate a certificate for gitlab.pomerium.localhost.io and note the path to the certificate authority (CA) root before proceeding.

TIP

Integrations that use unique subdomains will require their own certificates and Pomerium routes.

  1. Create the directory /srv/gitlab/config/ssl/ (adjusted for your local Docker volume path), and move the certificate and key files there:

    mkdir /srv/gitlab/config/ssl
    mv gitlab.pomerium.localhost.io.pem gitlab.pomerium.localhost.io-key.pem /srv/gitlab/config/ssl
    

    Note: You will need elevated permissions (sudo or root access) regardless of the directory location, as GitLab restricts permissions from within the Docker container on the volume mount.

  2. Adjust the docker-compose.yml entry for Pomerium to mount the internal certificate authority, and the entry for GitLab to specify the certificate and key files:

    services:
    
    ...
    
      pomerium:
        image: pomerium/pomerium:latest
        container_name: pomerium
        volumes:
          - ./srv/pomerium/config.yaml:/pomerium/config.yaml:ro
          - ~/.local/share/mkcert:/pomerium/ssl:ro # Adjust to the location of your internal certificate authority.
        ports:
          - 443:443
          - 80:80
    ...
    
    gitlab:
      container_name: gitlab
      image: gitlab/gitlab-ee:latest
      environment:
        GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.pomerium.localhost.io'
        letsencrypt['enable'] = false
        nginx['listen_port'] = 443
        nginx['listen_https'] = true
        nginx['ssl_certificate'] = "/etc/gitlab/ssl/gitlab.localhost.pomerium.io.pem"
        nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/gitlab.localhost.pomerium.io-key.pem"
      volumes:
        - '/srv/gitlab/config:/etc/gitlab'
        - '/srv/gitlab/logs:/var/log/gitlab'
        - '/srv/gitlab/data:/var/opt/gitlab'
      expose:
        - 80
        - 443
        - 22
      restart: always
      shm_size: '256m'
    
  3. Adjust the route in Pomerium's config.yaml to connect to GitLab using HTTPS:

      - from: https://gitlab.localhost.pomerium.io
        to: https://gitlab-ee
        preserve_host_header: true
        policy:
          - allow:
              or:
                - domain:
                    is: example.com
    
  4. Run docker-compose up -d to recreate the containers with the adjusted settings.