When logging into Portainer, your browser probably warned you about an untrusted connection due to the lack of an HTTPS URL. This can be solved by configuring Portainer to use a certificate.
There are plenty of internet tutorials that can guide you through creating a single, self-signed certificate. This step will concentrate on the more complex, but more flexible, self-hosted certificate authority. And by now, you should know it’ll be automated, so it won’t be too difficult.
By the end of this step you will have:
On a home network, where you trust your users, there’s no urgent need for encrypting web traffic with HTTPS. You can continue to use HTTP URLs for accessing everything. Or you can generate a single, self-signed certificate with openssl
in one command. But, creating a root CA and an intermediate certificate is a typical industry practice, so if nothing else, it provides a good learning opportunity.
vi subject-info.yml
ansible-playbook configure-certificate-authority.yml
ansible-playbook issue-host-certificate.yml
Setting up a certificate authority can be tedious. But, for this project there’s an Ansible playbook that will let you do this quickly and easily.
Here’s the procedure:
ansible-playbook configure-certificate-authority.yml
pi@mypi:~/cloudpi/ssl $ ansible-playbook configure-certificate-authority.yml
PLAY [Configure the root certificate authority (CA)] ****************************
TASK [Gathering Facts] **********************************************************
ok: [localhost]
TASK [Loading subject info] *****************************************************
ok: [localhost]
TASK [Creating directory to store signing requests] *****************************
changed: [localhost]
TASK [Generating the root certificate authority (CA) private key] ***************
changed: [localhost]
TASK [Generating a certificate signing request (CSR) for the root CA] ***********
changed: [localhost]
TASK [Signing the root certificate] *********************************************
changed: [localhost]
TASK [Generating the intermediate certificate private key] **********************
changed: [localhost]
TASK [Generating a CSR for the intermediate certificate] ************************
changed: [localhost]
TASK [Signing the intermediate certificate] *************************************
changed: [localhost]
PLAY RECAP **********************************************************************
localhost : ok=9 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When the playbook is done, you’ll have a root certificate called home_CA.crt and an intermediate certificate called home.crt. Both are plain text file in the /etc/ssl/certs/
directory, but they are encoded and won’t make much sense. You can verify the certificates and keys by using openssl
commands like the ones shown below.
$ openssl x509 -in /etc/ssl/certs/home_CA.crt -text -noout
$ sudo openssl rsa -in /etc/ssl/private/home_CA.key -check
$ openssl x509 -in /etc/ssl/certs/home.crt -text -noout
$ sudo openssl rsa -in /etc/ssl/private/home.key -check
After setting up the certificate authority, you’re ready to start issuing certificates. You have a couple choices.
The first method works fine if you plan to use URLs like https://mypi.home:9443 or https://mypi.home:8910 to access your applications. If you want to use friendlier names, like https://portainer.mypi.home or https://nextcloud.mypi.home and skip the port number, you’ll want to use the second method.
The Ansible playbook will generate a certificate that will work with all the DNS names used in this project.
To generate a certificate like this, use the Ansible playbook issue-host-certificate.yml
. It will produce a certificate that can be applied to the host DNS name as well as several other subdomains of the host.
pi@mypi:~/cloudpi/ssl $ ansible-playbook issue-host-certificate.yml
PLAY [Issue a certificate for multiple DNS names] *******************************
TASK [Gathering Facts] **********************************************************
ok: [localhost]
TASK [Loading subject info] *****************************************************
ok: [localhost]
TASK [Generating a private key] *************************************************
changed: [localhost]
TASK [Creating directory to store signing request] ******************************
ok: [localhost]
TASK [Generating a CSR] *********************************************************
changed: [localhost]
TASK [Signing the certificate] **************************************************
changed: [localhost]
PLAY RECAP **********************************************************************
localhost : ok=6 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
LDAP is not included in this multi-name certificate. It is configured separately in a later step.
With any self-signed certificate, your browser will complain about the certificate issuer not being trusted. That’s because the browser only has a couple dozen root certificates from the big name issuers that are in the trust store. With Firefox, you can add a certificate trust exception pretty easily.With Edge and others it’s a little harder, but not too bad.
Follow these instructions to get your Windows desktop to trust your self-hosted certificate authority:
/etc/ssl/certs
on the Pi. Where you put them on the Windows machine doesn’t matter.home_CA.crt
) and click Install Certificate…This covers any application that uses the Windows trust store. Firefox uses it’s own trust store, but it can be configured to use the Windows trust store as well. It involves setting the security.enterprise_roots.enabled parameter to true. There’s a Mozilla support article that gives more detail.
You must restart Firefox to apply the configuration change. Otherwise you will see This Connection is Untrusted or SEC_ERROR_BAD_SIGNATURE.
You can also manage your self-signed certificate using the the certificates snap-in in the Microsoft Management Console (mmc.exe). Refer to Microsoft’s Documentation for more information.
The trusted root certificate store on Raspberry Pi OS is defined by the certificates listed in /etc/ssl/certs/ca-certificates.crt
. This is a plain text file and you can easily add your self-signed certificate by appending the contents of the intermediate (home.crt) and root certificate (home_CA.crt) to ca-certificates.crt.
It may be tempting to skip this, thinking you’ll never use a web bowser on the Pi, so why bother? But, when two applications use a secure channel to communicate, they may expect a trusted root certificate.
If you started the Nginx Docker container for testing, you can run the playbook again now that the certificates have been generated. This time it won’t skip over the SSL tasks and you’ll have a web server that listens on HTTPS as well as HTTP.
Run ansible-playbook deploy-nginx-test.yml
again. The output should look like this:
pi@mypi:~/cloudpi $ ansible-playbook deploy-nginx-test.yml
PLAY [Deploy Nginx as a test instance] ******************************************
TASK [Gathering Facts] **********************************************************
ok: [localhost]
TASK [Installing apt key for Docker repository] *********************************
ok: [localhost]
TASK [Adding official repository] ***********************************************
ok: [localhost]
TASK [Installing Docker Community Edition] **************************************
ok: [localhost]
TASK [Installing Docker Compose] ************************************************
changed: [localhost]
TASK [Deploying Nginx container] ************************************************
changed: [localhost]
TASK [Checking for host certificate] ********************************************
ok: [localhost]
TASK [Checking for host key] ****************************************************
ok: [localhost]
TASK [Creating an alternate default.conf with SSL enabled] **********************
changed: [localhost]
TASK [Copying alternate default.conf to Nginx container] ************************
changed: [localhost]
TASK [Reloading nginx configuration] ********************************************
changed: [localhost]
PLAY RECAP **********************************************************************
localhost : ok=11 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Now go to https://mypi.home (replacing http with https) in a web browser. You should see the Nginx Welcome page and not see any complaints about untrusted certificates.
With DNS and a certificate authority, your users will be able to easily and securely access your applications using a familiar URL like https://mypi.home:port. The next step in the list of improvements will let them log in those applications with a single, consistent username and password. This is feature is enabled by installing OpenLDAP.
Life is too short to have anything but delusional notions about yourself. —Gene Simmons