SSL with self signed certificate

Hi here,

I run into an issue cloning a repository hosted on a GitHub Enterprise using a self signed certificate.

The workaround using a custom clone step with skip_verify: true is working, but I want to avoid this since I have the certificate on my host.

I tried running directly the drone/git docker image (since it’s the one use for the clone) and if I add the mount option -v /etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt:/etc/ssl/certs/ca-certificates.crt the ssl problem disappear.

So I tried to reproduce that with 2 tests on the drone server (single machine, no agents):

  • adding the environment var DRONE_VOLUME=/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt:/etc/ssl/certs/ca-certificates.crt
  • adding a volume --volume=/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt:/custom-ca-certificates.crt and the environment var DRONE_VOLUME=/custom-ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt
    But none of this has worked (SSL certificate problem: unable to get local issuer certificate).

Did I miss something here ? Is it possible to mount a custom certs bundle, and if so do someone knows how to do it ?

Drone 1.2.2 running on docker on a Red Hat Enterprise Linux Server release 7.6 (Maipo)

Thanks

Ok found the answer myself digging the code. You have to set the DRONE_RUNNER_VOLUMES env var and not the DRONE_VOLUME. Despite not being documented in server env vars, it’s available as written in the source code. That make sense since in single node the server is also a runner, but this would be nice if documented.

1 Like

Can you supply your final DRONE_RUNNERS_VOLUMES for me? I don’t know where to mount my cert file from my host into the Alpine Agent image.

Here is my configuration for docker-compose.

  drone-runner:
    image: drone/agent:1
    restart: always
    container_name: drone-runner
    volumes:
      - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_RPC_PROTO=https
      - DRONE_RPC_HOST=xxx
      - DRONE_RPC_SECRET=xxx
      - DRONE_RUNNER_CAPACITY=2
      - DRONE_RUNNER_VOLUMES=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt

It mounts the ca-certificates.crt file from the host into the runner and from there into the pipeline containers.

2 Likes

Aha, I was missing the part about actually mounting it into the agent itself. Great, thank-you!

Somehow I’m doing something wrong. And the variable mentioned has not been documented since.

Here is my docker-compose. I’m running my gitea behind a traefik with a lets encrypt certificate which does not have any issue to be verified from the docker host. But the agent does not like it. And gives an error on the checkout.

Initialized empty Git repository in /drone/src/.git/
+ git fetch origin +refs/heads/master:
fatal: unable to access 'https://git.example.com/drone-tests.git/': SSL certificate problem: self signed certificate  

version: '3'

services:

  drone:
    image: drone/drone:1
    container_name: drone_ci
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.drone.rule=Host(`drone.example.com`)"
      - "traefik.http.routers.drone.entrypoints=web-secure"
      - "traefik.http.routers.drone.tls=true"
      - "traefik.http.routers.drone.tls.certresolver=default"
      - "traefik.http.routers.drone.tls.domains[0].main=*.example.com"
    volumes:
      - drone:/data
      - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro
    environment:
      - DRONE_GITEA_CLIENT_ID
      - DRONE_GITEA_CLIENT_SECRET
      - DRONE_GITEA_SERVER
      - DRONE_GIT_ALWAYS_AUTH
      - DRONE_RPC_SECRET
      - DRONE_SERVER_HOST
      - DRONE_SERVER_PROTO
      - DRONE_LOGS_DEBUG=true
      - DRONE_RUNNER_VOLUMES=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt
    ports:
      - 80
      - 443
    networks:
      - traefik_default

  drone-runner-docker:
    image: drone/agent:1
    container_name: drone_runner_docker
    restart: unless-stopped
    environment:
      - DRONE_RPC_HOST
      - DRONE_RPC_PROTO
      - DRONE_RPC_SECRET
      - DRONE_RUNNER_VOLUMES=/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt
    networks:
      - traefik_default
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro

networks:
  traefik_default:
    external: true

volumes:
  drone:

For reference, the global volumes parameter is documented at DRONE_RUNNER_VOLUMES | Drone

Perhaps you need to mount the certs to a different target location? The location and format of the certificate files varies by linux distribution, and it looks like you are mounting to a target path and format that is optimized for unbuntu and debian distributions. The git clone plugin, however, uses an Alpine Linux image as the runtime environment for cloning your repository.

For reference, this snippet provides a list of certificate file locations, by distribution:

var certFiles = []string{
	"/etc/ssl/certs/ca-certificates.crt",                // Debian/Ubuntu/Gentoo etc.
	"/etc/pki/tls/certs/ca-bundle.crt",                  // Fedora/RHEL 6
	"/etc/ssl/ca-bundle.pem",                            // OpenSUSE
	"/etc/pki/tls/cacert.pem",                           // OpenELEC
	"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
	"/etc/ssl/cert.pem",                                 // Alpine Linux
}

Thanks! I’ll look into it and see if I can get it working. Its true I was working from a Ubuntu machine. It is strange to me that Lets Encrypt Root certs are not part of Alpine though.

Note that this method doesn’t work with CI jobs that for whatever reason, want to update the ca-certificate package, as the package manager will unable to overwrite the /etc/ssl/certs/ca-certificates.crt file.

My workaround is to mount the certificate elsewhere(for Alpine containers, /usr/local/share/ca-certificates/my-custom-root-ca.crt) and run whatever the command necessary to update the certificate store in the CI job script:

# docker-compose.yaml
services:
  drone-runner-docker:
    image: drone/drone-runner-docker:1
    environment:
      DRONE_RUNNER_VOLUMES: /path/to/my-custom-root-ca.crt:/usr/local/share/ca-certificates/my-custom-root-ca.crt
    volumes:
      - ./my-custom-root-ca.crt:/path/to/my-custom-root-ca.crt:ro

Alpine:

# deliver-artifacts-to-internal-https-server.sh
if ! update-ca-certificates; then
    printf \
        'Error: Unable to update root CA certificates.\n' \
        1>&2
    exit 2
fi

CentOS:

if ! \
    cp \
        /usr/local/share/ca-certificates/my-custom-root-ca.crt \
        /etc/pki/ca-trust/source/anchors/; then
    printf \
        'Error: Unable to copy the root CA certificate to the PKI trust anchors directory.\n' \
        1>&2
    exit 2
fi

if ! update-ca-trust; then
    printf \
        'Error: Unable to update root CA certificates.\n' \
        1>&2
    exit 3
fi

I hope that the process would be much simpler in the future, though, like import the certificates directly in the container’s entrypoint script.

UPDATE: It seems that DRONE_RUNNER_VOLUMES environment variable doesn’t support having multiple entries with the same source path mounted to multiple different paths in the container(only the last entry will apply), in that case one would need to copy the certificate to the right location in the CI job scripts as well.