Selfhosted GitLab in Docker with DS-Lite where fix-ip-provider already uses letsencrypt

Hello GitLab-Community,
I run a self-hosted GitLab using docker-compose (docker-compose.yml posted below), which is installed on a computer in my living room. I am connected to the internet via DS-Lite. The letsencrypt support is turned on and GitLab is availble from the Web via https://treffer-technologies.feste-ip.net/.
About a week ago my gitlab-runner suddently failed to connect due to a certification error. I suspected a hickup with the auto-renewal of the letsencrypt certificate. After 10+ hours of testing out numerous hints from the GitLab and LetsEncrypt Community without success I started to investigate this error in gitlab itself, not the runner:

    ================================================================================
    Error executing action `create` on resource 'letsencrypt_certificate[treffer-technologies.feste-ip.net]'
    ================================================================================

    RuntimeError
    ------------
    acme_certificate[staging] (letsencrypt::http_authorization line 43) had an error: RuntimeError: ruby_block[create certificate for treffer-technologies.feste-ip.net] (letsencrypt::http_authorization line 110) had an error: RuntimeError: [treffer-technologies.feste-ip.net] Validation failed, unable to request certificate, Errors: [{url: https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/3362084934/7XFM8w, status: invalid, error: {"type"=>"urn:ietf:params:acme:error:unauthorized", "detail"=>"The key authorization file from the server did not match this challenge \"RKPzvsY_ccw6aqr_U605vhOjrcv3yasjCXkX1xQ4bMI.7J-3s-TQuS3G4xwqmXCog5Psd_ed5r2wI7zTtpf8Tuo\" != \"RKPzvsY_ccw6aqr_U605vhOjrcv3yasjCXkX1xQ4bMI.oBNd5smO5vYJ4JXg-7VH8ZPOOgURgOqPb-Ffq1bGeKA\"", "status"=>403}} ]

As you can see, the filename of the challenge itself is identical, but the hash-value after the dot is not. What also struck me as odd was that my GitLab was still reachable with a good locking certificate from letsencrypt, but the auto-generated certificate in /etc/gitlab/config/ssl is self-signed (Issuer: CN = treffer-technologies.feste-ip.net).

As I mentioned above my internet provider uses DS-Lite. To workaround that and be able to have a fix URL for my gitlab I am using the DDNS and portmapper service of feste-ip.net (german for ‘fixed-ip’), hence the URL my GitLab is reachable from. As per docker-compose.yml, the container listens to port 30080 and 30443. But for letsencrypt support in gitlab to work, ports 80 and 443 have to be reachable. Therefore, I use HTTP(s)-Proxies from feste-ip.net, which allows me to map 80 → 30080 and 443 → 30443. With that http://treffer-technologies.feste-ip.net/ and https://treffer-technologies.feste-ip.net/ work.

Now I found out that feste-ip.net have setup their own letsencrypt for HTTPS-Proxy and they will not turn it off for me. This explains the non-matching hash-values since their proxy-server responde to the acme-challenge and holds the letsencrypt certificate.

This is how I envision my setup in my head:

After all that explanation, here my question:
How would your setup look like with these circumstances (gitlab publically available and hence using ssl, works with gitlab-runner, stuck on DS-Lite, very low budget)?
a) deactivate letsencrypt support in gitlab and rely on feste-ips letsencrypt
b) deactivate letsencrypt support in gitlab and use a self-signed certificate
c) something else

I’m pretty sure that gitlab-runner will give me a headache again once this is sorted out. For example, what certificate do I have to pass it in ordner to make a connection and later be able to use git-commands? But I guess I’ll cross that bridge when I reach it.

version: '3.7'
services:

    postgresql:
        ...

    redis:
        ...

    plantuml:
        ...

    gitlab:
        image: 'gitlab/gitlab-ce:latest'
        restart: always
        hostname: 'treffer-technologies.feste-ip.net'
        logging:
            ...
        links:
            - postgresql:postgresql
            - redis:redis
        environment:
            GITLAB_OMNIBUS_CONFIG: |
                ...
                # nginx - ssl
                nginx['redirect_http_to_https'] = true
                nginx['redirect_http_to_https_port'] = 80
                nginx['client_max_body_size'] = "350m"
                # other
                gitlab_rails['gitlab_shell_ssh_port'] = 22
                # https://docs.gitlab.com/omnibus/settings/ssl.html#lets-encrypt-integration
                letsencrypt['enable'] = true
                letsencrypt['auto_renew'] = true
                letsencrypt['auto_renew_hour'] = "12"
                letsencrypt['auto_renew_minute'] = "30"
                letsencrypt['auto_renew_day_of_month'] = "*/15"
                external_url 'https://treffer-technologies.feste-ip.net'
                ...
        ports:
            - "30080:80"
            - "30022:22"
            - "30443:443"
        volumes:
            - /opt/gitlab/config:/etc/gitlab:rw
            - /opt/gitlab/log:/var/log/gitlab:rw
            - /opt/gitlab/data:/var/opt/gitlab:rw
        depends_on:
            - postgresql
            - redis

    runner:
        image: 'gitlab/gitlab-runner:ubuntu'
        restart: always
        logging:
            driver: "json-file"
            options:
              max-size: "10m"
              max-file: "10"
        volumes:
            - /opt/gitlab-runner/config:/etc/gitlab-runner
            - /var/run/docker.sock:/var/run/docker.sock
        depends_on:
            - gitlab

Hi @myPanda

What doesn’t make sense is, if your GitLab Runner is connecting to GitLab via feste-ip.net, why it is complaining about invalid certificate? The certificate there is valid.

What is the value of url in config.toml?

Hi @balonik

Thank you for your reply. I agree with you, that is very strange. The url is “https://treffer-technologies.feste-ip.net/”.

In retrospect I assume a problem with the certificate I additionally passed to the runner using volumes and/or tls-ca-file. I had to do that for the docker-in-docker to work properly. Its hard for me to recall how the config.toml exactly looked like when I noticed the error because of all the tinkering I did.

My plan is to repair the runner once I know whether to implement a), b) or c). Do you have a suggestion?

For reference, here is my current config.toml: It is minimal (I think) and I will expand on it when necessary:

concurrent = 1
check_interval = 60
log_level = "debug"

[session_server]
  session_timeout = 1800

[[runners]]
  name = "hp-prodesk-docker-runner"
  url = "https://treffer-technologies.feste-ip.net/"
  token = "xxxxxxxxxxxxxx"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "alpine:3.16"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    pull_policy = ["if-not-present"]
    shm_size = 0

However, that one changed a lot in the last week due to my tinkering. I’m pretty sure at some point I also had

    volumes = ["/cache", "/opt/gitlab-runner/config/certs/ca.crt:/etc/gitlab-runner/certs/ca.crt:ro"]

and some variant of tls-ca-file.

As far as I understand self-signed cert is your only option to keep the traffic from feste-ip to your server encrypted.

So, I tried my luck using a self-signed certificate. Now I get 502 Bad Gateway / Protocol Error when opening GitLab.

I created a new Certificate following this guide: How To Create Self-Signed Certificates Using OpenSSL.
I guess the most important thing is that I used treffer-technologies.feste-ip.net as CN

openssl req -x509 \
            -sha256 -days 356 \
            -nodes \
            -newkey rsa:2048 \
            -subj "/CN=treffer-technologies.feste-ip.net/C=DE/L=Ulm" \
            -keyout rootCA.key -out rootCA.crt 

I put the treffer-technologies.feste-ip.net.crt and treffer-technologies.feste-ip.net.key into /etc/gitlab/ssl (acutally, into /opt/gitlab/config/ssl because of docker-compose). This is my current docker-compose.yml:

    gitlab:
        image: 'gitlab/gitlab-ce:latest'
        #image: 'gitlab-postgres-12:latest'
        restart: always
        hostname: 'treffer-technologies.feste-ip.net'
        logging:
            driver: "json-file"
            options:
              max-size: "10m"
              max-file: "10"
        links:
            - postgresql:postgresql
            - redis:redis
        environment:
            GITLAB_OMNIBUS_CONFIG: |
                external_url 'https://treffer-technologies.feste-ip.net'
                ...
                # nginx
                nginx['enable'] = true
                nginx['error_log_level'] = "debug"
                # nginx - SSL
                nginx['ssl_certificate'] = "/etc/gitlab/ssl/treffer-technologies.feste-ip.net.crt"
                nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/treffer-technologies.feste-ip.net.key"
                # nginx - redirect
                nginx['redirect_http_to_https'] = true
                nginx['redirect_http_to_https_port'] = 80
                nginx['client_max_body_size'] = "350m"
                # other
                gitlab_rails['gitlab_shell_ssh_port'] = 22
                letsencrypt['enable'] = false
                letsencrypt['auto_renew'] = false
        ports:
            - "30080:80"
            - "30022:22"
            - "30443:443"
        volumes:
            - /opt/gitlab/config:/etc/gitlab:rw
            - /opt/gitlab/log:/var/log/gitlab:rw
            - /opt/gitlab/data:/var/opt/gitlab:rw
        depends_on:
            - postgresql
            - redis

I checked the rails, nginx, sidekick, and a lot of other logs, but none of them seems to contain anything (last entry over an hour ago).
Do you have an idea what I did wrong?
Is is even correct to use treffer-technologies.feste-ip.net as external_url and name for crt and key?
Which logs should I check?

Thanks in advance :slight_smile:

I abandoned the ‘creating a self-signed certificate on my own’ route since I couldn’t get it to work. I am not a networking-guy, and it shows. I deleted the crt and key and reenabled letsencrypt in docker-compose.yml. Basically I’m back where I started 2 weeks ago with the letsencrypt-error in the log but gitlab at least being useable.
Of course the gitlab-runner problem still exists which, as @balonik said, doesn’t make sense.
I registered a new runner without any problems using this:

docker run --rm -v /opt/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner:alpine register \
  --non-interactive \
  --executor "docker" \
  --docker-image alpine:3.16 \
  --url "https://treffer-technologies.feste-ip.net/" \
  --registration-token "xxxxxxxx" \
  --description "hp-prodesk-docker-runner" \
  --tag-list "build,test,deploy" \
  --run-untagged="true" \
  --locked="false" \
  --docker-pull-policy="if-not-present"
Runtime platform                                    arch=amd64 os=linux pid=7 revision=32fc1585 version=15.2.1
Running in system-mode.

Registering runner... succeeded                     runner=xxxxxxxx
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"

It appears in the gitlab-ui, but the runner of course never made contact. I am not passing any crt, key or pem to the runner. Right now the error I am getting is

Feeding runners to channel                          builds=0
Dialing: tcp treffer-technologies.feste-ip.net:443 ...
WARNING: Checking for jobs... failed                runner=xxxxxxxx status=couldn't execute POST against https://treffer-technologies.feste-ip.net/api/v4/jobs/request: Post "https://treffer-technologies.feste-ip.net/api/v4/jobs/request": x509: certificate relies on legacy Common Name field, use SANs instead

The config.toml:

concurrent = 1
check_interval = 60
log_level = "debug"

[session_server]
  session_timeout = 1800

[[runners]]
  name = "hp-prodesk-docker-runner"
  url = "https://treffer-technologies.feste-ip.net/"
  token = "xxxxxxxx"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]
  [runners.docker]
    tls_verify = false
    image = "alpine:3.16"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    pull_policy = ["if-not-present"]
    shm_size = 0

Something new I found out and cannot make sense of: When I execute

echo | openssl s_client -connect treffer-technologies.feste-ip.net:443 

outside of docker I get this:

CONNECTED(00000003)
---
Certificate chain
 0 s:CN = treffer-technologies.feste-ip.net
   i:C = US, O = Let's Encrypt, CN = R3
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
 2 s:C = US, O = Internet Security Research Group, CN = ISRG Root X1
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
xxx
-----END CERTIFICATE-----
subject=CN = treffer-technologies.feste-ip.net

issuer=C = US, O = Let's Encrypt, CN = R3

---
...

Which I guess is the letsencrypt-certificate from feste-ip.net (please let me know if you need the rest). However, when I execute that command inside the gitlab or gitlab-runner container, I get:

Certificate chain
 0 s:CN = treffer-technologies.feste-ip.net
   i:CN = treffer-technologies.feste-ip.net
---
Server certificate
-----BEGIN CERTIFICATE-----
xxx
-----END CERTIFICATE-----
subject=CN = treffer-technologies.feste-ip.net

issuer=CN = treffer-technologies.feste-ip.net

---
...

And when I execute this command

host treffer-technologies.feste-ip.net

outside of docker, I get this:

treffer-technologies.feste-ip.net is an alias for de1.portmap64.net.
de1.portmap64.net has address 185.248.148.11

However, when I execute that command inside the gitlab or gitlab-runner container, I get:

treffer-technologies.feste-ip.net has address 172.18.0.5

172.18.0.5 is the ip address that docker assigned to the gitlab-container. And 172.18.0.6 is the address for the gitlab-runner-container.

Does that mean anything?

You probably also have such entries in /etc/hosts in each container. This is because you specify hostname: '...' in your docker-compose. I guess this is needed by GitLab itself to work properly.

So quickest solution for the GitLab Runner is to use http:// instead of https:// and disable the redirect on GitLab nginx. Maybe you can set such redirect on feste-ip.net to force HTTPS from outside.

Alternative way is to configure GitLab Runner to trust that expired certificate - Self-signed certificates or custom Certification Authorities | GitLab

I was unable to get this configuration to work. I tried a lot of different things without success. the fix-ip-provider suggested that gitlab and docker need to run with IPv6 for this to work.

After installing gitlab-runner on a different server I was able to connect and run wihtout problems. So I cut the gitlab-runner configuration out of the docker-compose and put it into a separate container. Now everything is working.