via letsencrypt.sh
Three cases,
- a ‘normal’ www domain:
www.filmfestapp.com
- a ‘normal’ subdomain where the naked domain is part of shared webspace:
drop.mro.name
- a proxy subdomain for a rails application (redmine):
developer.mro.name
General settings
TLS / bettercrypto.org
$ cat /etc/lighttpd/conf-enabled/11-tls-bettercrypto.conf
# based on https://github.com/BetterCrypto/Applied-Crypto-Hardening/blob/master/src/configuration/Webservers/lighttpd/10-ssl-dh.conf
$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.use-sslv2 = "disable"
ssl.use-sslv3 = "disable"
ssl.pemfile = "/etc/lighttpd/start-ssl-cert/2015/ssl.crt" # just a dummy (unused fallback)
ssl.honor-cipher-order = "enable"
# http://www.heise.de/security/meldung/Logjam-Attacke-Verschluesselung-von-zehntausenden-Servern-gefaehrdet-2657502.html
# https://weakdh.org/sysadmin.html
# http://www.heise.de/forum/heise-Security/News-Kommentare/Logjam-Attacke-Verschluesselung-von-zehntausenden-Servern-gefaehrdet/Re-Ich-kriege-jedes-Mal-die-Kraetze/posting-7775924/show/
include_shell "/etc/lighttpd/ssl/cipher-list.sh"
ssl.dh-file = "/etc/lighttpd/ssl/dh2048.pem"
# ssl.ec-curve = "secp521r1"
ssl.ec-curve = "secp384r1"
setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=15768000") # six months
}
letsencrypt.sh
$ cd /var/www
$ sudo git clone https://github.com/lukas2511/letsencrypt.sh
$ sudo chown -R $USER:www-data letsencrypt.sh
$ cd letsencrypt.sh
$ echo "filmfestapp.com www.filmfestapp.com" | sudo tee -a domains.txt
$ echo "mro.name drop.mro.name developer.mro.name" | sudo tee -a domains.txt
lighttpd / letsencrypt
$ cat /etc/lighttpd/conf-enabled/12-tls-letsencrypt.conf
$HTTP["scheme"] == "http" {
# https://github.com/letsencrypt/letsencrypt/issues/94#issuecomment-156695088
alias.url = (
"/.well-known/acme-challenge/" => "/var/www/letsencrypt.sh/.acme-challenges/"
)
}
$SERVER["socket"] == ":443" {
# letsencrypt / vhosts
# matches what is in /var/www/letsencrypt.sh/domains.txt
#
# https://www.digitalocean.com/community/tutorials/how-to-set-up-multiple-ssl-certificates-on-one-ip-with-lighttpd
$HTTP["host"] =~ "^(www\.)?filmfestapp\.com" {
ssl.pemfile = "/var/www/letsencrypt.sh/certs/filmfestapp.com/privcert.pem" # created by config.sh 'deploy_cert' hook!
ssl.ca-file = "/var/www/letsencrypt.sh/certs/filmfestapp.com/fullchain.pem"
}
$HTTP["host"] =~ "^(developer|drop)\.mro\.name" {
ssl.pemfile = "/var/www/letsencrypt.sh/certs/mro.name/privcert.pem" # created by config.sh 'deploy_cert' hook!
ssl.ca-file = "/var/www/letsencrypt.sh/certs/mro.name/fullchain.pem"
}
}
A ‘normal’ www domain: www.filmfestapp.com
Redirect http -> https
Other than above, we need a server.conv
that redirects non-https requests — except .well-known/acme-challenge
:
$ cat /var/www/lighttpd/www.filmfestapp.com/server.conf
# handle http -> https redirects
$HTTP["scheme"] == "http" {
$HTTP["url"] !~ "/(\.well-known)/" {
url.redirect = (".*" => "https://www.filmfestapp.com$0")
}
}
A ‘normal’ subdomain where the domain is part of shared webspace: drop.mro.name
Challenge deployment hook script
We need the above plus a hook in config.sh
to (un)deploy the challenges during certificate verification:
$ cat /var/www/letsencrypt.sh/config.sh
create_privcert_pem_and_deploy_challenge_hook() {
# ${HOOK} "deploy_challenge" "${altname}" "${challenge_token}" "${keyauth}"
# ${HOOK} "clean_challenge" "${altname}" "${challenge_token}" "${keyauth}"
# ${HOOK} "deploy_cert" "${domain}" "${BASEDIR}/certs/${domain}/privkey.pem" "${BASEDIR}/certs/${domain}/cert.pem" "${BASEDIR}/certs/${domain}/fullchain.pem"
echo " $0 $@"
if [ "deploy_cert" = "$1" ] ; then
echo " + $0 Creating privcert.pem for lighttpd..."
# https://github.com/lukas2511/letsencrypt.sh/pull/73
# https://github.com/letsencrypt/letsencrypt/issues/94#issuecomment-155746219
cat "$3" "$4" > "$(dirname "$3")/privcert.pem"
fi
# deploy+clean challenge to shared webhost via ftp
if [ "mro.name" = "$2" ]
then
# http://blog.mro.name/2015/03/key-based-ftp-authentication/
remote_dir="//.well-known/acme-challenge/"
rsa_keyfile="$HOME/.ssh/id_rsa"
if [ "deploy_challenge" = "$1" ] ; then
curl --upload-file "$3" --user ':' --output /dev/null --silent --show-error --key "${rsa_keyfile}" --pubkey "${rsa_keyfile}.pub" "sftp://${remote_dir}/" || echo " ! Failure: $@" 1>&2
elif [ "clean_challenge" = "$1" ] ; then
curl --quote "rm ${remote_dir}/$3" --user ':' --output /dev/null --silent --show-error --key "${rsa_keyfile}" --pubkey "${rsa_keyfile}.pub" "sftp://${remote_dir}/" || echo " ! Failure: $@" 1>&2
fi
fi
}
HOOK=create_privcert_pem_and_deploy_challenge_hook
A proxy domain for a rails application (redmine): developer.mro.name
$ cat /etc/lighttpd/conf-enabled/10-proxy.conf
# /usr/share/doc/lighttpd-doc/proxy.txt
server.modules += ( "mod_proxy" )
# http://blog.mro.name/2013/12/redmine-rails-puma-lighttpd/
# http://redmine.lighttpd.net/projects/lighttpd/repository/entry/branches/lighttpd-1.4.x/src/mod_proxy.c?utf8=?&rev=2920#L276
$HTTP["host"] == "developer.mro.name" {
$HTTP["url"] !~ "^/\.well-known/acme-challenge/" {
proxy.server = ( "" => (("host" => "127.0.0.1", "port" => 8082)))
$HTTP["scheme"] == "http" {
# capture vhost name with regex condition -> %0 in redirect pattern
# must be the most inner block to the redirect rule
# zless /usr/share/doc/lighttpd/configuration.txt.gz
url.redirect = (".*" => "https://developer.mro.name$0")
}
}
}
Ready!
With lighttpd global and vhost configuration set up, config.sh
and domains.txt
ready, we need just
$ /var/www/letsencrypt.sh/letsencrypt.sh --cron