Letsencrypt with Apache and Puppet
By
- 3 minutes read - 503 wordsThis week I set myself the task of getting my various SSL site under Puppet control. Like all the cool kids I use letsencrypt for these certs. I also want to get all the information from Hiera. There are a small collection of modules you need:
If you’re using R10K (which you should) then add the following to your Puppetfile
:
mod 'puppetlabs/apache', '1.4.1'
mod 'danzilio/letsencrypt', '1.0.0'
mod 'stahnma/epel'
mod 'hiera_resources',
:git => 'https://github.com/rnelson0/puppet-hiera_resources.git'
We need to do 3 things (in the right order):
- Create a non-SSL Virtualhost
- DNS must already point to the host
- Will redirect to the SSL Virtualhost
- Must not redirect the Letsencrypt acme-challenge
- The first time, this will need to respond via the default virtualhost
- Generate a certificate
- Create the SSL Virtualhost using this certificate
I cannot tell you how to do the DNS, but the rest we can do with Puppet.
I put all this in a dedicated local module. The only thing local about it is that it is not really useful for anyone other me. It is available though if you want to have a look at it. What this allows me to do is define my SSL and non-SSL vhosts seperately in Hiera, and also define my certs in hiera. I can then create relationships between them to define the order.
So, my site.pp
will contain (lablocal
is that local module):
hiera_include('classes')
class { 'lablocal::nonsslvhosts': }->
class { 'lablocal::letsencryptcerts': }->
class { 'lablocal::sslvhosts': }
That references 3 classes in the lablocal module:
nonsslvhosts.pp
class lablocal::nonsslvhosts {
hiera_resources('apache-nonssl-vhosts')
}
letsencryptcerts.pp
class lablocal::letsencryptcerts {
hiera_resources('letsencryptcerts')
}
sslvhosts.pp
class lablocal::sslvhosts {
hiera_resources('apache-ssl-vhosts')
}
None of that is very complicated. All of the clever stuff is happenning in the Hiera file for that node:
---
classes:
- apache
- epel
- letsencrypt
apache-nonssl-vhosts:
apache::vhost:
www.example.com-nonssl:
servername: www.example.com
port: 80
docroot: /var/www/html
redirectmatch_status: 301
redirectmatch_regexp: ^(?!/\.well-known/acme-challenge/).*
redirectmatch_dest: https://www.example.com$0
apache-ssl-vhosts:
apache::vhost:
www.example.com:
port: 443
servername: www.example.com
docroot: /var/www/www.example.com
ssl: true
ssl_chain: /etc/letsencrypt/live/www.example.com/chain.pem
ssl_key: /etc/letsencrypt/live/www.example.com/privkey.pem
ssl_cert: /etc/letsencrypt/live/www.example.com/cert.pem
proxy_pass:
-
path: '/'
url: 'http://10.1.0.15:8080/'
letsencrypt::email: [email protected]
letsencrypt::configure_epel: false
letsencrypt::manager_cron: true
letsencryptcerts:
letsencrypt::certonly:
www.example.com:
plugin: webroot
webroot_paths:
- /var/www/html
All this is colllected using the hiera_resources module. First we collect the nonssl vhosts. We create a vhost with using the default docroot that performs a permanent redirect to the https:// vhost. However, we add in an exception for /.well-known/acme-challenge/ so that the letsencrypt server can talk back to us to authorise the certificate.
When this is created, Apache will be scheduled for a restart. This does not actually happen, so the first time the certificate is created letsencrypt will actually come in via the default virtualhost, not this one. In the future though, when the certificate is renewed, it will be used and the exception is required.
Next we create the certificate itself using the webroot plugin and put the response in /var/www/html/
. It will also create a cron job to automatically renew the certificate.
Finally it can create the main Virtualhost which should probably not contain anything surprising.