UPDATE: Jenkins with HTTPS in a Docker Container

The previous post on this topic is still relevant, however, since my selection of a new reverse proxy, I felt it necessary to publish an update.

Assumptions

  • You know what HTTPS and SSL certificates are/do and how the CA ecosystem works
  • You have a domain that you plan to use for the Jenkins instance and this domain either doesn't have a CAA record or has one that allows LetsEncrypt
  • You understand the general working of containers and have docker installed on your system. Docker/Docker Compose knowledge is a plus but not required, you can basically just copy/paste code to get this working. I'll also go over some common pitfalls at the end
  • You either have your own reverse proxy setup and can handle this or you're using my Traefik config
  • You know what Jenkins is and why you should be using it. If you don't, use the Google machine

OK, let's get this show on the road.

Prerequisites

  • A linux environment - because there's a special circle of hell reserved for you if you're doing this on Windows (just kidding...)
  • Docker
  • Docker-compose
  • A domain and the ability to add A records
  • Tea/Coffee because caffeine

1 - Checking Your Install

At the time of writing, I've got the docker and docker-compose versions below. If these become wildly out of date I'll try to update in the future. Or shoot me an angry email on the About Me page and I'll get right to it! If you can't be bothered to read the walkthrough and just want the files, they're pasted at the bottom of the post. I gotchu.

putty_o6DddMQAfc

As long as your docker --version and docker-compose --version outputs are these versions or later, this should work fine. If they're not, check if you've got installed via snap or pip or something as they sometimes don't update versions frequently. Official docker install process is here if you need it

You can also run docker run hello-world to make sure containers are running properly.

putty_jxJIkm3Y8a

2 - Adding Your A Record

Before we get all the nifty LetsEncrypt bits done, you need to have a domain that resolved to the IP of your server/raspberry pi/nan's old laptop. Hopefully you've got a VPS with a public IPv4 adress you can use, if not get comfortable with port forwarding. You'll need to forward TCP 80 and 443 inbound because LetsEncrypt still uses 80 for one of the validation challenges.

Create an A record however the Good Lord GoDaddy lets you (or use a respectable registrar/DNS provider ;)). This can be on the root domain or any subdomain, we're not getting fancy and doing wildcard certificates here, I'll probably cover that in another post.

chrome_87S0Xpuq4d

3 - Compose Like Vivaldi

OK so here's where we get into the docker-compose magic. There's another post explaining the proxy in detail so if you'd like to read up on that, check it out here. You can grab the correct proxy config from that of this page and just docker-compose up -d that bad boy. Therefore, we'll jump straight to the Jenkins YAML file.

version: '3'

services:
  jenkins:
    container_name: jenkins
    image: jenkins/jenkins:lts
    restart: unless-stopped

We start with the basics, version and services because we're keeping to the standard like good, rule followers™. Next, we name the service and container something sensible and pull the jenkins/jenkins:lts image. You can use the :latest tag instead of :lts if you want the bleeding edge releases but I'd like my build server to be nice stable personally... restart: unless-stopped has the advantage of restarting the container automatically if you reboot the OS of the machine that it's running on but doesn't get too aggressive if there's a critical error that you need to stop the container for.

labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jenkins.entrypoints=https"
      - "traefik.http.routers.jenkins.rule=Host(`sub.domain.tld`)"
      - "traefik.http.routers.jenkins.tls.certresolver=le"
      - "traefik.http.services.jenkins.loadbalancer.server.port=8080"

Labels are how we tell Traefik how to route traffic for this container. Because Jenkins for some reason won't let you change the port that it's running on, you always have to proxy across to 8080. The loadbalancer label means that the proxy will take 443 traffic (designated by the entrypoint=https (more on this in the post about my Traefik setup)) on whatever the Host URL is and forward it to 8080 on the container so Jenkins is none the wiser. Neat!

volumes:
      - jenkins_home:/var/jenkins_home
    networks:
      - traefik_proxy

volumes:
  jenkins_home:

networks:
  traefik_proxy:
    external: true

Next there's the volume goodness. Fairly standard, nothing special here. If you want to get creative and map a specific local place in the filesystem you can change the named mapping to a bind mount, as detailed in the docker docs. We have to declare this named volume at the bottom by itself because we're being rule followers™, remember?

4 - Run Forrest, Run

Now, it's time to put the butler to work. Worth mentioning that you should have your proxy up and running by this point, docker-compose up -d if you dont. docker-compose up -d && docker-compose logs -f will do the trick. It'll grab the relevant images, start like magic on the first attempt and then show you the logs of a fledgling jetty server doing it's thing. You'll also, crucially, see the below little chunk of text.

putty_LPHrWb089c

Grab that password and browse to the domain that you've pointed at this server. If you get some weird browser error, give it 30 seconds, dump the cache and refresh. The proxy container probably just took a second to get all the certs in order. If all went well, you should be greeted with the below screen.

chrome_MGZMUnVkVA

You guessed it, stick that admin password in and hit Continue!

chrome_ROFWJqDtow

This one depends on if you know what you're doing, I normally just hit the 'Install suggested plugins' options. You can always pick other specific ones later on.

chrome_p71HRp8S36

Then some nifty plugin installing will commence. This will take a minute so grab a cup of tea. Containerising without tea is uncivilised, you should know that already!

chrome_FGkU6gwGW2

Now you'll create an admin user. Bob the builder is really the only user that makes sense in the context of creating a build server but if you have some other cool name like Jeffrey or Federcio then go ahead and enter that instead. I can't stop you from making the wrong decisions, I can only try and advise you against them... Also we know Bob The Builder is the type of dude to still have an AOL email account, don't @ me.

chrome_A6RCHkIec6

This next page should have picked up your install URL because you've browsed to it but if not, pop the FQDN in here to avoid any wrongly genearted links in the future.

chrome_OgXoQRdcf1

Congratuwelldone! The butler is at your service.

chrome_SJ5a3K4EP7

Can you smell that? That new server smell? Smells like hope with notes of long nights figuring out why it builds locally but Jenkins hates you. That's all yet to come, friends. I'll be writing more Jenkins posts as I set up build pipelines in various differnet environments so keep your eyes peeled for those scintillating pieces of writing.

Song of the Post: My favourite from the new Wise Man's Fear record. Has some 'current year' vibes too...