Selfhosting a cloud. Part 2: Secure your services with HTTPS

Selfhosting a cloud

  1. Basic setup
  2. Reverse Proxy with Traefik v2
  3. Secure your services with HTTPS (this post)
  4. Nextcloud - everything you need and more
  5. Some nice services you might want
  6. Use keycloak for user management and SSO

Disclaimer

Due to how disorganised I am, there will be a lot of tangents and (more or less) off-topic paragraphs.

Any paragraph that looks like this is one of those paragraphs. You can ignore them and information on this page will still make sense.

I will sometimes use these paragraphs to explain something in more details.

This post assumes you read the previous post

I know there was at least one person waiting for this post. It was supposed to come out soon after the last one and I really wanted to write it. Unfortunately, life happened and I was busy for the last month. So if you were waiting all this time, I just wanted to say Thank you and I'm sorry it took so long.

SSL, TLS, HTTPS and all the other abbreviations

I'm not an expert on security or encryption, so I'll give only a brief overview on what HTTPS is.

In short, HTTPS encrypts all the traffic between your browser and the server. This makes it almost impossible for a third party to see what data you're sending and receiving from the server. This doesn't mean that they can't see where the traffic is going. So, for example, your ISP can see that you're visiting your bank's website, but they can't see your password.

HTTPS uses SSL certificates signed by a certificate authority (CA) to encrypt that traffic. I don't know exactly how it works, but I know there is a lot of math involved. As for the certificate authority, you technically can sign the certs yourself, but it's discouraged since it would allow everyone to pretend to be whoever they want. There is over one hundred CAs, though (according to Wikipedia) 98.5% of websites use one of just 5 CAs. We'll be using Let's Encrypt as our CA, which is free for everyone and trusted by every browser and OS. Additionally, we'll be using a wild card cert so that a single certificate works for all sub domains.

Adding HTTPS to Traefik

Define a certificate resolver

A certificate resolver is what requests, renews and applies a certificate for use with HTTPS. Edit your traefic.yml file and add the following code block to it:


certificatesResolvers:
    acme:
        acme:
        email: "mail@example.com"
        storage: "/letsencrypt/acme.json"
        # Staging server to make sure everything works
        caserver: "https://acme-staging-v02.api.letsencrypt.org/directory"
        dnsChallenge:
            provider: DNS_PROVIDER
Replace the email with your email. For the DNS_PROVIDER use a valid provider from this list. Note the environment variables needed for configuration. You will later set them to correct values you get from your DNS provider. Clicking on the "Additional configuration" link will usually give you some more information about those variables and where to get their values from, as well as some optional variables you can set if you want to.

In the example block above we're using the staging server instead of the main one. The reason for that is rate limiting. In short, you can only request so many certificates in a short period of time before you get blocked. The staging server allows for a lot more requests before getting blocked. Since it's likely that you will request a certificate multiple times, either because you made a mistake or you want to do it again, you'd hit the limit pretty quickly, I know from experience.

Open the docker-compose file for Traefik and add the following mapping to the volumes array:


- ./data/acme.json:/letsencrypt/acme.json

You also have to provide one or more environment variables for the DNS provider. I recommend using a .env file for that. Add the following section to the traefik container description in docker-compose:


env_file:
    - ./proxy.env
Now put the relevant variables in the file proxy.env, for example if you use Hetzner DNS:

HETZNER_API_KEY=abcdef123456789ABCDEF

Configure domains for HTTPS

To configure which domains get the certificate you need to add the following labels to your Traefik container:


- "traefik.http.router.proxy.tls.certresolver=acme"
- "traefik.http.router.proxy.tls.domains[0].main=example.com"
- "traefik.http.router.proxy.tls.domains[0].sans=*.example.com"
If you want to add other domains (I have draganczuk.me and draganczuk.cloud domains) just copy-paste last two lines and increase the number in square brackets near domains

Globar redirect for HTTPS

Since we're using HTTPS, let's make sure people don't use regular HTTP. The following labels on trafik container will redirect every HTTP request to HTTPS:


- "traefik.http.router.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.router.http-catchall.entrypoints=http"
- "traefik.http.router.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
In short, this creates a special router called http-catchall. The rule means that the target host (domain) is literally anything and the entrypoint is http (defined in traefik.yml to be port 80). This router applies a middleware called redirect-to-https. This middleware is of type redirectscheme and the scheme it redirects to is https.

TL;DR: Anything that targets this server using HTTP is redirected to HTTPS.

Create a container file for certificates

Now that HTTPS is mostly configured it's time to create a file where the certificates will be stored. The following command will create the file and set correct permissions:


touch data/acme.json && chmod 600 data/acme.json
If the permissions were any different, Traefik would complain and not work.

Since we're using the staging server we'll have to delete and recreate this file later, so keep that in mind.

Set other services to use HTTPS

Since we didn't have HTTPS earlier, the other services (like whoami) only listen on HTTP. We need to change that. Luckily, it's really easy. Just go to each service and modify the label traefik.http.routers.<ROUTER>.entrypoints and change it's value from http to https.

You also need to add a label to enable HTTPS:


- "traefik.http.routers.<ROUTER>.tls=true"

Update and restart your services by going to each service's folder and running


docker-compose down && docker-compose up -d
This will make sure all containers are restarted and their labels updated. Technically running just docker-compose up -d should work, but we just want to make sure.

Testing

HTTPS should now work, although your browser will most likely still show an error about wrong certificate authority. This is normal at this stage. Open your service in a browser, click on the padlock in the address bar and click on "Certificate". This will show details on your certificate.

If you see a certificate talking about "Let's Encrypt" then everything is working fine.

If you see "TRAEFIK DEFAULT CERT" anywhere in this window, then restart Traefik with docker-compose restart and try again. If you still get the traefik default cert then you probably have a typo somewhere. Enable debug logging for traefik by adding the following block to traefik.yml:


log:
    level: DEBUG
Restart traefik and look in the logs with docker-compose logs.

Get a proper certificate

Now that everything works, go to your traefik.yml and remove the line starting with caserver: . Now delete the data/acme.json file and create it using the command above. Restarting traefik now will create the proper certificate.

Configure TLS to get a good grade

If you were to test the SSL score right now using something like this tool you are likely to get a mediocre grade. This means that there are potential security issues. While we could leave it like that without many issues, fixing this is really easy.

Create a file called tls.yml in data/extra-conf folder. As the content paste this block:


tls:
    options:
        default:
            minVersion: VersionTLS12
            sniStrict : true
            cipherSuites:
                - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
                - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
                - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
                - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
                - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
                - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

Restart traefik to make sure it picked up the new configuration and run the SSL test again. You should now get an A.

Conclusion

That's all you need to secure your services with HTTPS. We will be using the certificate you just got for every service. The thing I love about traefik and wild card certs is that it's a "set and forget" type of software. Once it's working you don't really need to do anything else. Just add more services, paste the labels and it'll just work.