GitLab CE - tips and tricks

Tags: #<Tag:0x00007f0cabfa6428> #<Tag:0x00007f0cabfa62e8> #<Tag:0x00007f0cabfa61a8> #<Tag:0x00007f0cabfa6068> #<Tag:0x00007f0cabfa5f28>


##Table of Contents

Use cases - my lab, my rules

If I want to post some personal files and host private projects cloud services aren’t my first choices for InfoSec work. Sure, there are many. GitHub, BitBucket… Codeplex… what’s next?

My use cases include committing various archives for Malware Analysis and Forensics. These projects remain private, and I also do not want that some weird AV deletes my work.
Cloud hosters can just delete security-critical content, if they think that it poses risks to their customers and their services. Sometimes the ToS prohibit to commit Malware related content. It doesn’t matter why you do that. It’s not your space anyways.

In order to keep a distance, I host my own GitLab CE server. It’s packed with features, freely available, and useful as a self-hosted service. And it’s in my lab, where I set the rules.

Here’re my tips and tricks.

Works for me - works for you

Here are some service config tricks.

GitLab CE behind an Apache Reverse Proxy

If you have a web-server, or two, as intermediates - as Reverse Proxies - you need to configure them appropriately. The GitLab documentation about this topic scattered, which is why I document my approach here:

  • I use an Apache2 ReverseProxy, but an Nginx or Lighttpd will do as well.
  • I have a GitLab VM guest, with an internal IP within the block.

Apache2 ReverseProxy with x-fer headers for GitLab CE

Here is what I put in the sites-enabled (Debian style config layout) for Apache2:

<VirtualHost *:80>

    SSLProxyEngine On
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerExpire off

    ProxyPreserveHost Off
    ProxyRequests Off
    ProxyPass /
    ProxyPassReverse /

    RequestHeader set X_FORWARDED_PROTO 'https'
    RequestHeader set X-Forwarded-Ssl on

    CustomLog /var/log/apache2/code_apache_proxy_https_access.log combined
    ErrorLog /var/log/apache2/code_apache_proxy_https_error.log

The Apache will listen on the external IP, and pass incoming HTTP requests along to the internal IP on port 80. It’s an intermediary as well. I have another service in front for the TLS handling.

I recommend that you take a look at Pound or HAProxy for this. Or let a free CDN service handle this, like I do for now. The point is to split the encryption handling into another layer.

In GitLab CE I needed to change the hostname to, to my FQDN to be precise. In the gitlab.rb this can look like this:

[email protected]:/etc/gitlab# cat gitlab.rb | grep -v "#" | grep external
external_url ''

In some cases you may need to make sure that the internal host gets the FQDN, either via an /etc/hosts entry or via split DNS.

This might come in handy as well for your gitlab.rb

gitlab_rails['gitlab_hostname'] = ''
gitlab_rails['gitlab_ssh_host'] = ''

You might want to continue to use SSH from the internal (VPN) network only. Using IPs directly is messy with Git, because of the SSL certificates. If you it that way the GitLab WebUI will display the correct URLs as well.

Result: you can run GitLab behind a Reverse Proxy. It’s supported and easy to do. For now it’s not well documented anywhere, but here I think.

Using self-signed SSL certs for an internal GitLab CE node

Even though Letsencrypt may make this obsolete for 99% of all cases, here is the workflow for that. It’s basically the SSL private key -> CSR (Certificate Signing Request) -> CRT (Certificate) procedure.

First of all we need to disable the letsencrypt support for GibLab:

[email protected]:/etc/gitlab# cat gitlab.rb | grep -v "#" | grep letsencrypt
letsencrypt['enable'] = false

Then we need to generate the the self-signed certificate chain.

openssl genrsa -out "" 2048
openssl req -new -key "" -out ""
openssl x509 -req -days 365 -in "" -signkey ""  -out ""

The FQDN of the cert must fit the service (and hostname). The key needs to be 2048 bits (not 4096). The default is, that you place the certificates into the folder:

[email protected]:/etc/gitlab/ssl# ls | grep example

Now keep in mind that there is a naming convention for the Chef / gitlab-ctl tools. You need to name the files with the FQDN.$suffix.

For my internet-facing setup this SSL key does not matter. It does matter for certain GitLab services, like the Docker Registry and the CI / CD pipelines.

Sign GitLab commits

Assuming you have uploaded the respective public key, you need to ensure that the meta-infos of the key match with the GitLab account you are using (same for GitHub):

Git repo config:

    git config "marius"
    git config "[email protected]"

GPG key meta-infos:

    ➜  tfx1 git:(master) ✗ gpg --list-secret-keys --keyid-format LONG
    sec   rsa4096/FEECFC2D782843C9 2019-01-11 [SC]
    uid                 [ultimate] Because Security <[email protected]>
    ssb   rsa4096/B2071658FD3FD937 2019-01-11 [E]

Select the respective key for the local repo:

➜  tfx1 git:(master) ✗ git config user.signingkey B2071658FD3FD937

Then sign the commit, either via the command-line

➜  tfx1 git:(master) ✗ git commit -S -m "Initial commit

On MacOS X, if you use Homebrew, you may have to correct the path for pinentry

➜  tfx1 git:(master) ✗ which pinentry

➜  tfx1 git:(master) ✗ more ~/.gnupg/gpg-agent.conf
default-cache-ttl 600
max-cache-ttl 7200
pinentry-program /usr/local/bin/pinentry

Now let’s sign the commit on the command-line:

➜  tfx1 git:(master) ✗ export GPG_TTY=$(tty)
➜  tfx1 git:(master) ✗ git commit -S -m "Initial commit"
➜  tfx1 git:(master) ✗ git push origin master


  • 17.05.2018 - first version
  • 23.12.2018 - updates, corrected English language
  • 14.01.2019 - added infos on how to sign commits