GitLab
GitLab 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 Begin
-
This guide is written for an environment using Docker Compose.
-
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.
cautionWhile 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
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 from GitLab's docs as needed, or let us know if we need to re-visit this section.
Prepare The Environment
-
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.
-
Create three sub-directories:
config
,data
, andlogs
:mkdir -p /srv/gitlab/{config,data,logs}
Install and Configure GitLab
-
Edit your
docker-compose.yml
file to define a new service for GitLab:docker-compose.ymlservices:
---
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
andregistry_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.
tipAdditional integrations like Mattermost and Pages will require additional configuration (i.e.
mattermost_nginx[*]
). - Adjust
-
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 fromhealth:starting
tohealthy
.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
-
An additional route will provide an encrypted TCP tunnel through which users can securely access code using Git:
config.yaml- from: tcp+https://gitlab.localhost.pomerium.io
to: tcp://gitlab:22
policy:
- allow:
or:
- claim/groups: devs@example.com -
Once this route is applied, users can create an encrypted connection using pomerium-cli or the Pomerium Desktop app:
pomerium-cli tcp gitlab.pomerium.localhost.io:22 --listen :2202
The
--listen
key defines what port the tunnel listens at locally. -
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, 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, 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 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 to generate testing certificates:
-
Create a certificate for the domain that will be used for the GitLab route. For example:
mkcert "gitlab.pomerium.localhost.io"
-
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.
Integrations that use unique subdomains will require their own certificates and Pomerium routes.
-
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/sslNote: 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.
-
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:docker-compose.ymlservices:
...
pomerium:
image: cr.pomerium.com/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' -
Adjust the route in Pomerium's
config.yaml
to connect to GitLab using HTTPS:config.yaml- from: https://gitlab.localhost.pomerium.io
to: https://gitlab-ee
preserve_host_header: true
policy:
- allow:
or:
- domain:
is: example.com -
Run
docker-compose up -d
to recreate the containers with the adjusted settings.