I’ve been using the official Let’s Encrypt client, now Certbot on GitHub, using the webroot plugin, but then I saw Andreas Gohr’s blog post Simple Let's Encrypt on Debian/Apache, based on the German post letsencrypt.sh, based on Lukas Schauer’s letsencrypt.sh on GitHub.
=> now Certbot on GitHub | using the webroot plugin | Simple Let's Encrypt on Debian/Apache | letsencrypt.sh | letsencrypt.sh on GitHub
Setting it up required a bit of fiddling.
I created the so-called BASEDIR /etc/letsencrypt.sh
, with an empty domains.txt
file, the suggested hook.sh
and a config
(no longer config.sh
!) as follows:
hook.sh:
#!/bin/bash if [ ${1} == "deploy_cert" ]; then echo " + Hook: Restarting Apache..." service apache2 reload else echo " + Hook: Nothing to do..." fi
config:
BASEDIR="/etc/letsencrypt.sh/" WELLKNOWN="/var/www/letsencrypt.sh/" PRIVATE_KEY="${BASEDIR}/private_key.pem" HOOK="${BASEDIR}/hook.sh" CONTACT_EMAIL="kensanata@gmail.com"
I also created /var/www/letsencrypt.sh/
. These are the permissions and ownership I’m looking for:
drwxr-xr-x 2 www-data www-data 4096 May 31 12:50 letsencrypt.sh
I followed the Import from official letsencrypt client instructions. In the bash file, I had to set BASEDIR="/etc/letsencrypt.sh"
or it wouldn’t work, and I had to run it as root. This created the certs
subdirectory in my BASEDIR with all the certificates and it populated the domains.txt
file. I had to look over the list of domains because five out of six domains didn’t have the www
subdomain I wanted. Probably because the very first time I made those calls I had forgotten about them.
=> Import from official letsencrypt client
The perl file just required me to cpanm --sudo Crypt::OpenSSL::RSA
and cpanm --sudo Crypt::OpenSSL::Bignum
. The result of calling it I piped into private_key.pem
in my BASEDIR.
For all my sites, I replaced all the .pem
locations.
Old:
SSLEngine on SSLCertificateFile /etc/letsencrypt/live/alexschroeder.ch/cert.pem SSLCertificateKeyFile /etc/letsencrypt/live/alexschroeder.ch/privkey.pem SSLCertificateChainFile /etc/letsencrypt/live/alexschroeder.ch/chain.pem SSLVerifyClient None
New:
SSLEngine on SSLCertificateFile /etc/letsencrypt.sh/certs/alexschroeder.ch/cert.pem SSLCertificateKeyFile /etc/letsencrypt.sh/certs/alexschroeder.ch/privkey.pem SSLCertificateChainFile /etc/letsencrypt.sh/certs/alexschroeder.ch/chain.pem SSLVerifyClient None
Do this for all sites in sites-enabled
.
Don’t forget to place the following in /etc/apache2/conf.d/letsencrypt
:
Alias /.well-known/acme-challenge /var/www/letsencrypt.sh/Options None AllowOverride None Order allow,deny Allow from all
When I ran ~/src/letsencrypt.sh/letsencrypt.sh -c
the first time, I got an error, presumably because I had forgotten to restart Apache and so the challenge wasn’t being served:
alex@kallobombus:~/src/letsencrypt.sh$ sudo ~/src/letsencrypt.sh/letsencrypt.sh -c 1. INFO: Using main config file /etc/letsencrypt.sh/config Processing alexschroeder.ch with alternative names: www.alexschroeder.ch + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: alexschroeder.ch + Configured names: alexschroeder.ch www.alexschroeder.ch + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:16:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for alexschroeder.ch... + Requesting challenge for www.alexschroeder.ch... + Hook: Nothing to do... + Responding to challenge for alexschroeder.ch... + Hook: Nothing to do... ERROR: Challenge is invalid! (returned: invalid) (result: { ... })
When I tried to sudo service apache2 reload
I got an error in my config. The problem was that my config was referring to the chain.pem
but the import script hadn’t copied it over!
I was forced to run something like the following: cd /etc/letsencrypt.sh/certs; for d in *; do echo $d; cp -i /etc/letsencrypt/live/$d/chain.pem /etc/letsencrypt.sh/certs/$d/chain-1464687792.pem; cd /etc/letsencrypt.sh/certs/$d; ln -s chain-1464687792.pem chain.pem; done
(I had picked the timestamp by looking at the other files).
Finally reloading Apache worked.
Just to be sure, I created a little text file as /var/www/letsencrypt.sh/test
and tried to read it using https://alexschroeder.ch/.well-known/acme-challenge/test
. That seemed to work.
And now it worked!
alex@kallobombus:~$ sudo ~/src/letsencrypt.sh/letsencrypt.sh -c 1. INFO: Using main config file /etc/letsencrypt.sh/config Processing alexschroeder.ch with alternative names: www.alexschroeder.ch + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: alexschroeder.ch + Configured names: alexschroeder.ch www.alexschroeder.ch + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:16:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for alexschroeder.ch... + Requesting challenge for www.alexschroeder.ch... + Hook: Nothing to do... + Responding to challenge for alexschroeder.ch... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.alexschroeder.ch... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done! Processing arabisch-lernen.org with alternative names: www.arabisch-lernen.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 6 17:27:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing campaignwiki.org with alternative names: www.campaignwiki.org + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: campaignwiki.org + Configured names: campaignwiki.org www.campaignwiki.org + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:17:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for campaignwiki.org... + Requesting challenge for www.campaignwiki.org... + Hook: Nothing to do... + Responding to challenge for campaignwiki.org... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.campaignwiki.org... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done! Processing communitywiki.org with alternative names: www.communitywiki.org + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: communitywiki.org + Configured names: communitywiki.org www.communitywiki.org + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:17:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for communitywiki.org... + Requesting challenge for www.communitywiki.org... + Hook: Nothing to do... + Responding to challenge for communitywiki.org... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.communitywiki.org... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done! Processing korero.org with alternative names: www.korero.org + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: korero.org + Configured names: korero.org www.korero.org + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:18:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for korero.org... + Requesting challenge for www.korero.org... + Hook: Nothing to do... + Responding to challenge for korero.org... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.korero.org... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done! Processing oddmuse.org with alternative names: www.oddmuse.org + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: oddmuse.org + Configured names: oddmuse.org www.oddmuse.org + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:18:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for oddmuse.org... + Requesting challenge for www.oddmuse.org... + Hook: Nothing to do... + Responding to challenge for oddmuse.org... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.oddmuse.org... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done! Processing orientalisch.info with alternative names: www.orientalisch.info + Checking domain name(s) of existing cert... changed! + Domain name(s) are not matching! + Names in old certificate: orientalisch.info + Configured names: orientalisch.info www.orientalisch.info + Forcing renew. + Checking expire date of existing cert... + Valid till Jun 10 09:18:00 2016 GMT (Less than 30 days). Renewing! + Signing domains... + Generating private key... + Generating signing request... + Requesting challenge for orientalisch.info... + Requesting challenge for www.orientalisch.info... + Hook: Nothing to do... + Responding to challenge for orientalisch.info... + Hook: Nothing to do... + Challenge is valid! + Hook: Nothing to do... + Responding to challenge for www.orientalisch.info... + Hook: Nothing to do... + Challenge is valid! + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + Hook: Restarting Apache... [ ok ] Reloading web server config: apache2. + Done!
Note the message saying that Apache is reloading.
And to verify it, I use curl
and look for the line saying “expire date”:
alex@kallobombus:~$ curl --verbose --head --silent https://alexschroeder.ch/ * About to connect() to alexschroeder.ch port 443 (#0) * Trying 127.0.0.1... * connected * Connected to alexschroeder.ch (127.0.0.1) port 443 (#0) * successfully set certificate verify locations: * CAfile: none CApath: /etc/ssl/certs * SSLv3, TLS handshake, Client hello (1): * SSLv3, TLS handshake, Server hello (2): * SSLv3, TLS handshake, CERT (11): * SSLv3, TLS handshake, Server key exchange (12): * SSLv3, TLS handshake, Server finished (14): * SSLv3, TLS handshake, Client key exchange (16): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSLv3, TLS change cipher, Client hello (1): * SSLv3, TLS handshake, Finished (20): * SSL connection using ECDHE-RSA-AES128-GCM-SHA256 * Server certificate: * subject: CN=alexschroeder.ch * start date: 2016-05-31 09:32:00 GMT * expire date: 2016-08-29 09:32:00 GMT * subjectAltName: alexschroeder.ch matched * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. ...
Yay!
Now all I have to do is install this.
I copied /home/alex/src/letsencrypt.sh/letsencrypt.sh
to /etc/letsencrypt.sh/letsencrypt.sh
and created the file /etc/cron.weekly/letsencrypt.sh
which calls it:
#!/bin/sh exec /etc/letsencrypt.sh/letsencrypt.sh -c
I made sure to grant it +x
permissions, too. 🙂
And now, just to test things, let’s run the cron job:
alex@kallobombus:~$ sudo /etc/cron.weekly/letsencrypt.sh 1. INFO: Using main config file /etc/letsencrypt.sh/config Processing alexschroeder.ch with alternative names: www.alexschroeder.ch + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:32:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing arabisch-lernen.org with alternative names: www.arabisch-lernen.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 6 17:27:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing campaignwiki.org with alternative names: www.campaignwiki.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:32:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing communitywiki.org with alternative names: www.communitywiki.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:32:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing korero.org with alternative names: www.korero.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:32:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing oddmuse.org with alternative names: www.oddmuse.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:33:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do... Processing orientalisch.info with alternative names: www.orientalisch.info + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 29 09:33:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do...
Very nice.
#Web #Administration #Cryptography #Dehydrated
(Please contact me if you want to remove your comment.)
⁂
I really should read the draft for the ACME protocol (via their GitHub).
=> draft for the ACME protocol | their GitHub
– Alex Schroeder 2016-05-31 13:56 UTC
I did the same for Emacs Wiki. Since I’m using nginx as the front-end, there are small differences. Here’s the excerpt from /etc/nginx/sites-enabled/emacswiki
:
server { listen 443 ssl; server_name www.emacswiki.org po6.ferrier.me.uk; # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.4.6&openssl=1.0.1f&hsts=yes&profile=modern # using Nginx, Modern, Server Version 1.4.6 and OpenSSL Version 1.0.1f with HSTS ssl_certificate /etc/letsencrypt.sh/certs/www.emacswiki.org/fullchain.pem; ssl_certificate_key /etc/letsencrypt.sh/certs/www.emacswiki.org/privkey.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits # using openssl dhparam -out dhparam.pem 2048 ssl_dhparam /etc/nginx/dhparam.pem; # modern configuration. tweak to your needs. ssl_protocols TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'; ssl_prefer_server_ciphers on; # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) # are we ready? # add_header Strict-Transport-Security max-age=15768000; # OCSP Stapling --- # fetch OCSP records from URL in ssl_certificate and cache them ssl_stapling on; ssl_stapling_verify on; # verify chain of trust of OCSP response using Root CA and Intermediate certs # https://community.letsencrypt.org/t/will-does-the-letsencrypt-client-create-a-cert-chain-usable-with-ocsp-stapling/2072/15 ssl_trusted_certificate /etc/letsencrypt/live/www.emacswiki.org/chain.pem; ... location /.well-known/acme-challenge { alias /var/www/letsencrypt.sh/; } }
And /etc/letsencrypt.sh/hook.sh
is also different, of course:
#!/bin/bash if [ ${1} == "deploy_cert" ]; then echo " + Hook: Reloading Nginx..." service nginx reload else echo " + Hook: Nothing to do..." fi
And the output:
1. INFO: Using main config file /etc/letsencrypt.sh/config Processing www.emacswiki.org with alternative names: emacswiki.org + Checking domain name(s) of existing cert... unchanged. + Checking expire date of existing cert... + Valid till Aug 14 06:27:00 2016 GMT (Longer than 30 days). Skipping renew! + Hook: Nothing to do...
– Alex Schroeder 2016-06-10 09:16 UTC
Oh, the project renamed itself to dehydrated.
=> dehydrated
– Alex Schroeder 2017-03-14 06:21 UTC
text/gemini
This content has been proxied by September (3851b).