Some notes on the configuration of my setup
- All services accessible from the internet run in jails (all jails reside in ''%%/usr/jails%%'' by default on %%FreeBSD%%)
- I use [[/LibreSSL]] ([[http://freshports.org/security/libressl-devel|LibreSSL port]])
- I use [[http://www.zsh.org/|The Z Shell]] ([[http://freshports.org/shells/zsh|zsh port]])
Things that don't need to run as ''%%root%%'' will be running as an unprivileged user. I will use the user ''%%_letsencrypt%%'' with group ''%%_letsencrypt%%'' as the unprivileged user that will perform the certificate renewal process. Deployment of the keys and certificates will have to be executed with a privileged user, this guide uses ''%%root%%''.
====== Install Letsencrypt.sh ======
The port is available in the ports tree. Install it using the official pkg repository using
pkg install letsencrypt.sh
or alternatively (e.g. when you want to use ZSH) build your own using [[https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-poudriere.html|Poudriere]] or any of the other building-from-source options and install it. The port works with either ''%%zsh%%'' or ''%%bash%%''.
This will install the actual script into ''%%/usr/local/bin%%''. Configuration will land in ''%%/usr/local/etc/letsencrypt.sh%%'' as will the keys, certificates and certificate-chains. You should want to check that the configuration directory is not world-writable.
====== Prepare user & directories ======
To make life easier all of the challenges (%%LetsEncrypt%% as well as keybase etc) will be hosted in a shared dir ''%%/usr/local/www/.well-known%%'' on the jail running my Apache server. The %%LetsEncrypt%% bits will land in ''%%/usr/local/etc/letsencrypt.sh%%'' on the host system. There's no requirement to run the interaction with the %%LetsEncrypt%% servers as root, so these will run as a non-privileged user. This unprivileged user will have to write to the ''%%acme-challenge%%'' and the directory that will contain the keys and certificates.
pw groupadd -n _letsencrypt -g 443
pw useradd -n _letsencrypt -u 443 -g 443 -d /usr/local/etc/letsencrypt.sh -w no -s /nonexistent
chown root:_letsencrypt /usr/local/etc/letsencrypt.sh
chmod 770 /usr/local/etc/letsencrypt.sh
mkdir -p -m 775 /usr/jails/http/usr/local/www/.well-known/acme-challenge
chgrp _letsencrypt /usr/jails/http/usr/local/www/.well-known/acme-challenge
Adapt the ''%%.well-known/acme-challenge%%'' directory to your situation.
====== Modify Apache configuration ======
The acme validation will ''%%GET%%'' a uniquely named file from ''%%http:///.well-known/acme-challenge/%%''
Access to the ''%%.well-known%%'' directory is granted in my main Apache config file ''%%/usr/local/etc/apache24/httpd.conf%%''
Options None
AllowOverride None
Require all granted
Header add Content-Type text/plain
The Content-Type header was in my configs somewhere, shouldn't hurt.\\
If you want to only share the ACME challenges you can suffix ''%%.well-known/%%'' with ''%%acme-challenge/%%''
Now every (non-ssl) Virtual Host that I have gets a on-line addition
Alias /.well-known/ /usr/local/www/.well-known/
You need to make sure that all (sub-)domains that you want to sign have access to this directory!\\
That includes rewrites etc.\\
The acme validation is done only using **plain http** and will not honour redirects etc.
====== Letsencrypt configuration ======
===== Domains to sign =====
The script requires a list of domain names you want to have a SAN cert for in the following format:
example.com www.example.com
example.net www.example.net wiki.example.net
Domains and sub-domains that are listed on the //same line// will result in SAN-certificates ([[https://en.wikipedia.org/wiki/SubjectAltName|Subject-Alternative-Name]]).\\
Store this as ''%%/usr/local/etc/letsencrypt.sh/domains.txt%%''
The default configuration file requires some changes, these are stored in ''%%/usr/local/etc/letsencrypt.sh/config.sh%%''
BASEDIR="/usr/local/etc/letsencrypt.sh"
WELLKNOWN="/usr/jails/http/usr/local/www/.well-known/acme-challenge"
alias openssl='/usr/local/bin/openssl'
To make use of %%LibreSSL%% (or %%OpenSSL%% from ports) you must add the ''%%alias openssl%%'' to the config script.
===== Configure periodic job =====
The ''%%security/letsencrypt.sh%%'' port includes a periodic job that makes automation very simple. To automatically renew certificates, add the following to your ''%%/etc/periodic.conf%%''
weekly_letsencrypt_enable="YES"
weekly_letsencrypt_user="_letsencrypt"
weekly_letsencrypt_deployscript="/usr/local/etc/letsencrypt.sh/deploy.sh"
====== First run ======
You will probably want to run %%LetsEncrypt%% manually the first time
cd /usr/local/etc/letsencrypt.sh
su -m _letsencrypt -c 'zsh /usr/local/bin/letsencrypt.sh --cron'
(Replace zsh with bash if that's the shell you have installed)
You will end up with a sub-directory ''%%certs%%'' that contains your domains as directories with the Subject-Alternative-Names certs and the corresponding private keys. The symbolic links are useful for deployment.
example.com/
cert-1453573903.csr
cert-1453573903.pem
cert.csr -> cert-1453573903.csr
cert.pem -> cert-1453573903.pem
chain-1453573903.pem
chain.pem -> chain-1453573903.pem
fullchain-1453573903.pem
fullchain.pem -> fullchain-1453573903.pem
privkey-1453573903.pem
privkey.pem -> privkey-1453573903.pem
example.net/
cert-1453576309.csr
cert-1453576309.pem
cert.csr -> cert-1453576309.csr
cert.pem -> cert-1453576309.pem
chain-1453576309.pem
chain.pem -> chain-1453576309.pem
fullchain-1453576309.pem
fullchain.pem -> fullchain-1453576309.pem
privkey-1453576309.pem
privkey.pem -> privkey-1453576309.pem
====== Deploy new certs ======
We've run the certificate request process as a restricted user but you'll need to run the deploy script as ''%%root%%''.
Here you'll probably need to get creative with scripting. In the host environment, your now have
/usr/local/etc/letsencrypt.sh/certs/example.net/privkey.pem
/usr/local/etc/letsencrypt.sh/certs/example.net/fullchain.pem
NB: %%%%''%%These are symlinks!!!%%''%%%%\\
===== Example (jailed) applications =====
Your Apache server may (should?) run in the ''%%http%%'' jail and you've setup an Apache Virtual Host with
SSLCertificateFile /etc/ssl/certs/example.net.pem
SSLCertificateKeyFile /etc/ssl/priv/example.net.pem
and your %%OpenSMTPd%% mailserver for example.net in the ''%%mail%%'' jail
pki example.net certificate "/etc/ssl/certs/example.net.pem"
pki example.net key "/etc/ssl/priv/example.net.pem"
listen on $lan_addr port 587 tls-require \
pki example.net hostname example.net auth
Seen from the host environment your certificates actually need to end up in
/usr/jails/```http```/etc/ssl
/usr/jails/```mail```/etc/ssl
===== Example deployment script =====
You could use the following script to deploy new certs
#!/bin/sh
domain="example.net"
letsencryptdir="/usr/local/etc/letsencrypt.sh"
targets="mail http"
for jail in ${targets}; do
targetdir="/usr/jails/${jail}/etc/ssl"
# Check if the certificate has changed
[ -z "`diff -rq ${letsencryptdir}/certs/${domain}/fullchain.pem ${targetdir}/certs/${domain}.pem`" ] && continue
cp -L "${letsencryptdir}/certs/${domain}/privkey.pem" "${targetdir}/priv/${domain}.pem"
cp -L "${letsencryptdir}/certs/${domain}/fullchain.pem" "${targetdir}/certs/${domain}.pem"
chmod 400 "${targetdir}/priv/${domain}.pem"
chmod 644 "${targetdir}/certs/${domain}.pem"
# Restart/-load relevant services
[ "${jail}" = "http" ] && jexec ${jail} service apache24 restart
[ "${jail}" = "mail" ] && jexec ${jail} service smtpd restart
done
# Clean up old keys and certs
/usr/local/bin/letsencrypt.sh --cleanup
Store this as ''%%/usr/local/etc/letsencrypt.sh/deploy.sh%%'' and make sure the execute bit is set.\\
**NB:** Some applications want a private key, certificate and separate chain instead. If this is the case you'll need to copy ''%%cert.pem%%'' and ''%%chain.pem%%'' to the appropriate location in stead.