Setup Harbor with Let's Encrypt
Tags: docker, tutorial, harbor, letsencrypt
Published: 2021-01-23 13:12:34

This tutorial provides a step-by-step guide to setup [Harbor](https://goharbor.io/) with a [Let’s Encrypt](https://letsencrypt.org/) certificate using [Certbot](https://certbot.eff.org/)

# Requirements

- Linux machine _(tested with Ubuntu 20.04)_
- A public domain pointing to your Linux machine
- Port 80/443 is reachable from the outside
- Docker
- Docker Compose

# Certbot / Let’s Encrypt

First, we need to install Certbot to create Let’s Encrypt certificates on our machine. On Ubuntu, this can be easily done by using snap:

```sh
$ snap install certbot --classic
```

Afterward, we are able to create a new certificate for our Harbor domain by using the standalone mode of Certbot and add the desired domain as a parameter. When running Certbot for the first time, it will ask for a valid email address and accept the terms of service before creating the certificate.

```sh
$ certbot certonly --standalone -d registry.example.com
```

Once the command has been completed, it should provide us the following output:

```
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
  /etc/letsencrypt/live/registry.example.com/fullchain.pem
  Your key file has been saved at:
  /etc/letsencrypt/live/registry.example.com/privkey.pem
  Your certificate will expire on 2021-04-21. To obtain a new or
  tweaked version of this certificate in the future, simply run
  certbot again. To non-interactively renew *all* of your
  certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
Donating to EFF:                    https://eff.org/donate-le
```

Finally, we can find the certificate files in the folder of the related domain at **/etc/letsencrypt/live**.

```sh
$ ls /etc/letsencrypt/live/registry.example.com
README  cert.pem  chain.pem  fullchain.pem  privkey.pem
```

# Harbor

Now we can continue with installing Harbor on the machine. Let’s go to the [Harbor GitHub Releases page](https://github.com/goharbor/harbor/releases), download the latest installer and extract the archive:

```sh
$ wget https://github.com/goharbor/harbor/releases/download/v2.1.3/harbor-online-installer-v2.1.3.tgz
$ tar xvf harbor-online-installer-v2.1.3.tgz
```

To configure the Harbor instance, we can create a copy of the extracted Harbor configuration template and open it with an editor:

```sh
$ cd harbor
$ cp harbor.yml.tmpl harbor.yml
$ vim harbor.yml
```

The configuration file provides a lot of properties to configure and customize the Harbor instance. For a minimalistic installation, it’s enough to change the following values:

```yaml
# Change the hostname to your domain configured earlier
hostname: reg.mydomain.com
# Keep the http and https port configuration, but change the path of
# the certificate files to the Let's Encrypt certificate
http:
  port: 80
https:
  port: 443
  certificate: /your/certificate/path
  private_key: /your/private/key/path
# Change the admin password to something more secure
harbor_admin_password: Harbor12345
# Change the database password to something more secure
database:
  password: root123
```

Once the configuration has been adapted, Harbor is ready to be deployed using the install script:

```sh
$ ./install.sh
...
✔ ----Harbor has been installed and started successfully.----
```

Finally, let’s open the domain of the Harbor instance in a browser and check the result. We should see the Harbor login page secured by a valid certificate.

# Certificate Renewal

Certbot creates a Cronjob/Timer to renew the requested certificates automatically before they expire.

```sh
$ systemctl list-timers
Fri 2021-01-22 07:10:00 CET 5h 57min left Thu 2021-01-21 22:20:02 CET 2h 52min ago snap.certbot.renew.timer     snap.certbot.renew.service
```

Currently, this renewal will fail, because the Nginx container of Harbor is already using port 80 on the machine. This port is needed by Certbot to renew the existing certificates.

Fortunately, Certbot provides Pre-/Post-Hooks which can be created to stop/start running services before/after renewing the certificates.

These hooks can be created at **/etc/letsencrypt/renewal-hooks/{pre|post}**. Let’s create a script to stop the Nginx container of Harbor before running the renew task:

```
$ vim /etc/letsencrypt/renewal-hooks/pre/harbor.sh

Insert the following content:

#!/bin/bash
/usr/bin/docker stop nginx

Make the script executable:

$ chmod 755 /etc/letsencrypt/renewal-hooks/pre/harbor.sh
```

Now we also have to create the post-hook script to copy the new renewed certificates to Harbor’s data directory and start the Nginx container again. Create the post-hook script and add the following content:

```
$ vim /etc/letsencrypt/renewal-hooks/post/harbor.sh

Insert the following content:

#!/bin/bash
cp -f /etc/letsencrypt/live/registry.example.com/fullchain.pem /data/secret/cert/server.crt
cp -f /etc/letsencrypt/live/registry.example.com/privkey.pem /data/secret/cert/server.key
/usr/bin/docker start nginx

Make the script executable:

$ chmod 755 /etc/letsencrypt/renewal-hooks/post/harbor.sh
```

Keep in mind to change the paths according to your setup. The configured data directory for Harbor can be found in the harbor.yml file.

To check if the scripts are configured correctly, we can test the renewing of the certificate by using Certbot’s renew command in dry-run mode. We should see the following output:

```sh
$ certbot renew --dry-run
...
Running pre-hook command: /etc/letsencrypt/renewal-hooks/pre/harbor.sh
Output from pre-hook command harbor.sh:
nginx
...
Running post-hook command: /etc/letsencrypt/renewal-hooks/post/harbor.sh
Output from post-hook command harbor.sh:
nginx
```

Now the setup is completed. The Harbor instance should be up and running and the Let’s Encrypt certificate should be automatically renewed.