3

Please note that this question relates to personal use of a dedicated server, not professional.

I want to run two GitLab Docker Containers on the same machine with two different volumes, the two of them "made available" on port 443 on the hosts' machine. Port 80 for http content is not made available. The host's HTTP will not be a 3xx redirect, it will be a web page using HSTS. SSH ports on the hosts' will be something like 10703 and 10803.

The urls will be https://gitlab.example.com and https://sources.example.com. The certificates are maintained by Let's Encrypt on the host.

The content will be served by Apache, using mod_proxy and virtual hosts. Apache does not run inside Docker. There are other virtual hosts enabled unrelated to GitLab. In order to simplify certificates, I'm trying not to put certificates inside the gitlab themselves. Instead the Apache configuration holds the certificates.

Now here come the issues.

  • If I specify https://gitlab.example.com as the external_url:

    • https will be enabled. gitlab will refuse to even start because the certificates are missing. As I said I'm using mod_proxy, I don't need certificates because Apache is doing all the work already. I would like GitLab to serve insecure content locally to Apache so that it's Apache's job to make that secure over untrusted network.
  • If I specify http://gitlab.example.com as the external_url and let Apache forward to http://127.0.0.1:10701, which is port 80 on the Docker Container:

    • almost all gitlab web resources will be served through http://gitlab.example.com, causing the browser to indicate the site is in practice insecure, which is understandable.
    • the copy and paste link to clone a gitlab repository will be http://gitlab.example.com/group/something.git, causing the clone links to fail because it's not https.
  • SSH is forwarded from the port 10703 on the host's machine. Inside the Docker Container, it's running on port 22. The current SSH cloning copy and paste link is still git@gitlab.example.com:group/something.git. I want it to be ssh://git@gitlab.example.com:10703/group/something.git (see answer about cloning on other ports)


My X problem is:

  • To serve GitLab web interfaces securely on the standard https port of the host (443).

  • To preserve the usability of the copy and paste links of the web interface content, with no compromise on security.

  • Constraint: Apache must not be replaced.

My current Y ideal solution is:

  • I would like to strongly decouple GitLab's configuration from intent. Currently when I configure the external URL to https, it recognizes the intent to be serving secure content, which requires certificates. When in fact, I just want the external URL to change. Same deal with SSH, separate external displayed port (used for copy and paste links) from actual networking port from the container's perspective. Maybe there is a configuration that allows this.

My current Y quick and dirty solutions are:

  • Use https://... as the external url, and add placeholder certificate files to /etc/gitlab/ssl/. GitLab will ignore these certificates completely, but as long as they are present in the filesystem, GitLab will be able to start and the host will be able to deliver secure content. I would like to avoid doing this if there is a better alternative.

  • To solve the SSH problem, Maybe I could add gitlab_rails['gitlab_shell_ssh_port'] = 10703 (see answer about changing SSH port) and then use docker run --publish 10703:10703 ... instead of how it's currently done ( docker run --publish 10703:22 ... ). EDIT: It turns out that gitlab_rails['gitlab_shell_ssh_port'] only changes the displayed port. Since it's sshd that manages the port 22 and not gitlab, docker run --publish 10703:10703 ... will cause port 10703 on the host to be forwarded to port 10703 on the container, which is closed. docker run --publish 10703:22 ... + gitlab_rails['gitlab_shell_ssh_port'] = 10703 is how it should be done.

How can I solve this problem? Both elegant and quick and dirty ways are appreciated.

Community
  • 1
  • 1
Hay
  • 2,246
  • 20
  • 30

1 Answers1

2

After 4 months, I'm going to answer my question in a way that does not answer the original question but provide how I managed to perform what I wanted so far, for any reader who may be stumbling upon this question.

I've done this a while ago so the contents of this answer may not be correct, and it could also be bad practice, but I wouldn't know.

Remember this question relates to personal use of a dedicated server, not professional.


Container configuration

There are no guarantees this configuration is sufficient to have a removable container with data preservation. Use this at your own risk.

This is my docker run command:

docker create \
    --name example-gitlab \
    --restart=unless-stopped \
    --hostname gitlab.example.com \
    --publish 2222:22 \
    --env GITLAB_OMNIBUS_CONFIG="external_url 'https://gitlab.example.com/'; gitlab_rails['gitlab_signup_enabled'] = false; gitlab_rails['gitlab_shell_ssh_port'] = 2222; nginx['real_ip_trusted_addresses'] = [ '172.17.0.0/16' ]; nginx['real_ip_header'] = 'X-Forwarded-For'" \
    --volume /data/docker-data/example-gitlab/config:/etc/gitlab \
    --volume /data/docker-data/example-gitlab/logs:/var/log/gitlab \
    --volume /data/docker-data/example-gitlab/data:/var/opt/gitlab \
    gitlab/gitlab-ce:latest

I'm not sure the --hostname gitlab.example.com \ serves any purpose.

SSH port exposure and UI display

GitLab doesn't care about the SSH port in its logic. The SSH port provided in the environment variables is only for display purposes, when displaying the SSH URL of a repository to the user.

  • I've exposed the real port 22 of the container onto port 2222: --publish 2222:22
  • I've passed to GitLab the port number to display: gitlab_rails['gitlab_shell_ssh_port'] = 2222

HTTPS

As for https, I must put https:// in the environment variable. It must not be http. If it is http, the page will not be served securely as almost all resources and links will be through http, and some third-party resources will also be http.

When adding https to the environment variable, GitLab WILL check for valid TLS certificates.

I was unable to find a way around this, so I gave up and now I'm feeding GitLab with real certificates.

Since it's a personal server, I'm using an external script to copy the Let's Encrypt certificates into the volume I've exposed through the docker run command above.

cp /data/docker-data/http-realm/certs/live/gitlab.example.com/cert.pem /data/docker-data/example-gitlab/config/ssl/gitlab.example.com.crt
cp /data/docker-data/http-realm/certs/live/gitlab.example.com/privkey.pem /data/docker-data/example-gitlab/config/ssl/gitlab.example.com.key

Not pretty, but it works.


Reverse proxy

There has been a major change from the original question since that now I'm using Apache inside Docker. This causes Apache to gain access to Docker's internal DNS resolution.

This is my virtual host configuration using Apache. I could have used nginx but I'm using something I'm confident working with.

<IfModule mod_ssl.c>
        <VirtualHost *:443>
                ServerAdmin webmaster@localhost
                ServerName gitlab.example.com

        ## REQUIRED so that external resources such as gravatar are fetched
        ## using https by the underlying nginx wizard stuff
        #RequestHeader set X-Forwarded-Proto "https"
        Header add X-Forwarded-Proto "https"

        ## http://stackoverflow.com/questions/6764852/proxying-with-ssl
        SSLProxyEngine On

        RewriteEngine On
        ProxyRequests     Off
        ProxyPreserveHost On


        ProxyAddHeaders On
        ## docker alias
        ProxyPass / https://example-gitlab:443/
        <Location />
          ProxyPassReverse /
          Require all granted
        </Location>

        SSLEngine on

        SSLCertificateFile      /etc/letsencrypt/live/gitlab.example.com/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/gitlab.example.com/privkey.pem
        SSLCertificateChainFile /etc/letsencrypt/live/gitlab.example.com/chain.pem

        SSLProtocol all -SSLv2 -SSLv3
        SSLHonorCipherOrder on
        SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"



        # Custom log file locations
        ErrorLog /var/log/apache2/example-gitlab_error.log
        CustomLog /var/log/apache2/example-gitlab_access.log combined

        </VirtualHost>
</IfModule>

In the line ProxyPass https://example-gitlab:443/: the hostname example-gitlab is resolvable through Docker's own DNS because it is the container's name. I'm using Docker 1.12 in case that's a behavior specific to this version.

Therefore I don't need to publish port 443 nor port 80 of my GitLab container to the host, my reverse proxy takes care of this.

Network layers and X-Forwarded-For

The gitlab needs to be setup to trust the X-Forwarded-For header corresponding to your network layer.

Otherwise, in the admin panel, all users' IPs will seem to be coming from within the docker network layer.

If you are using a network layer of this type:

docker network create --driver=bridge \
  --subnet=192.168.127.0/24 --gateway=192.168.127.1 \
  --ip-range=192.168.127.128/25 strawberry

I believe you will need to change the docker run configuration and replace this:

nginx['real_ip_trusted_addresses'] = [ '172.17.0.0/16' ]

With this:

nginx['real_ip_trusted_addresses'] = [ '192.168.127.128/25' ]

(in addition to adding --net=strawberry in the docker run configuration)

Also if you are using nginx, you will probably have to switch this:

nginx['real_ip_header'] = 'X-Forwarded-For'

With this:

nginx['real_ip_header'] = 'X-Real-IP'
Hay
  • 2,246
  • 20
  • 30