lighttpd + letsencrypt.sh

Mon, 21. Dec 2015

Categories: en sysadmin Tags: curl Debian FTP Keys letsencrypt lighttpd redmine security SSH TLS

via letsencrypt.sh

Three cases,

General settings

TLS / bettercrypto.org

 1$ cat /etc/lighttpd/conf-enabled/11-tls-bettercrypto.conf 
 2# based on https://github.com/BetterCrypto/Applied-Crypto-Hardening/blob/master/src/configuration/Webservers/lighttpd/10-ssl-dh.conf 
 3$SERVER["socket"] == ":443" {
 4    ssl.engine = "enable"
 5    ssl.use-sslv2 = "disable"
 6    ssl.use-sslv3 = "disable"
 7    ssl.pemfile = "/etc/lighttpd/start-ssl-cert/2015/ssl.crt" # just a dummy (unused fallback)
 8    ssl.honor-cipher-order = "enable"
 9    
10    # http://www.heise.de/security/meldung/Logjam-Attacke-Verschluesselung-von-zehntausenden-Servern-gefaehrdet-2657502.html
11    # https://weakdh.org/sysadmin.html
12    # 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/
13    include_shell "/etc/lighttpd/ssl/cipher-list.sh"
14    ssl.dh-file = "/etc/lighttpd/ssl/dh2048.pem"
15
16    # ssl.ec-curve = "secp521r1"
17    ssl.ec-curve = "secp384r1"
18    setenv.add-response-header  = ( "Strict-Transport-Security" => "max-age=15768000") # six months
19}

letsencrypt.sh

1$ cd /var/www
2$ sudo git clone https://github.com/lukas2511/letsencrypt.sh
3$ sudo chown -R $USER:www-data letsencrypt.sh
4$ cd letsencrypt.sh
5$ echo "filmfestapp.com www.filmfestapp.com" | sudo tee -a domains.txt
6$ echo "mro.name drop.mro.name developer.mro.name" | sudo tee -a domains.txt

lighttpd / letsencrypt

 1$ cat /etc/lighttpd/conf-enabled/12-tls-letsencrypt.conf 
 2$HTTP["scheme"] == "http" {
 3  # https://github.com/letsencrypt/letsencrypt/issues/94#issuecomment-156695088
 4  alias.url = (
 5    "/.well-known/acme-challenge/" => "/var/www/letsencrypt.sh/.acme-challenges/"
 6  )
 7}
 8
 9$SERVER["socket"] == ":443" {
10  # letsencrypt / vhosts
11  # matches what is in /var/www/letsencrypt.sh/domains.txt
12  #
13  # https://www.digitalocean.com/community/tutorials/how-to-set-up-multiple-ssl-certificates-on-one-ip-with-lighttpd 
14  $HTTP["host"] =~ "^(www\.)?filmfestapp\.com" {
15    ssl.pemfile = "/var/www/letsencrypt.sh/certs/filmfestapp.com/privcert.pem"       # created by config.sh 'deploy_cert' hook!
16    ssl.ca-file = "/var/www/letsencrypt.sh/certs/filmfestapp.com/fullchain.pem"
17  }    
18  $HTTP["host"] =~ "^(developer|drop)\.mro\.name" {
19    ssl.pemfile = "/var/www/letsencrypt.sh/certs/mro.name/privcert.pem"              # created by config.sh 'deploy_cert' hook!
20    ssl.ca-file = "/var/www/letsencrypt.sh/certs/mro.name/fullchain.pem"
21  }
22}

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:

1$ cat /var/www/lighttpd/www.filmfestapp.com/server.conf
2# handle http -> https redirects
3$HTTP["scheme"] == "http" {
4  $HTTP["url"] !~ "/(\.well-known)/" {
5    url.redirect = (".*" => "https://www.filmfestapp.com$0")
6  }
7}

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:

 1$ cat /var/www/letsencrypt.sh/config.sh
 2
 3create_privcert_pem_and_deploy_challenge_hook() {
 4  # ${HOOK} "deploy_challenge" "${altname}" "${challenge_token}" "${keyauth}"
 5  # ${HOOK} "clean_challenge" "${altname}" "${challenge_token}" "${keyauth}"
 6  # ${HOOK} "deploy_cert" "${domain}" "${BASEDIR}/certs/${domain}/privkey.pem" "${BASEDIR}/certs/${domain}/cert.pem" "${BASEDIR}/certs/${domain}/fullchain.pem"
 7
 8  echo "  $0 $@"
 9
10  if [ "deploy_cert" = "$1" ] ; then
11    echo " + $0 Creating privcert.pem for lighttpd..."
12    # https://github.com/lukas2511/letsencrypt.sh/pull/73
13    # https://github.com/letsencrypt/letsencrypt/issues/94#issuecomment-155746219
14    cat "$3" "$4" > "$(dirname "$3")/privcert.pem"
15  fi
16
17  # deploy+clean challenge to shared webhost via ftp
18  if [ "mro.name" = "$2" ]
19  then
20    # http://blog.mro.name/2015/03/key-based-ftp-authentication/
21    remote_dir="/<webroot>/.well-known/acme-challenge/"
22    rsa_keyfile="$HOME/.ssh/id_rsa"
23    if [ "deploy_challenge" = "$1" ] ; then
24      curl --upload-file "$3"            --user '<remote_ftp_user>:' --output /dev/null --silent --show-error --key "${rsa_keyfile}" --pubkey "${rsa_keyfile}.pub" "sftp://<ftp_host>${remote_dir}/" || echo " ! Failure: $@" 1>&2
25    elif [ "clean_challenge" = "$1" ] ; then
26      curl --quote "rm ${remote_dir}/$3" --user '<remote_ftp_user>:' --output /dev/null --silent --show-error --key "${rsa_keyfile}" --pubkey "${rsa_keyfile}.pub" "sftp://<ftp_host>${remote_dir}/" || echo " ! Failure: $@" 1>&2
27    fi
28  fi
29}
30
31HOOK=create_privcert_pem_and_deploy_challenge_hook

A proxy domain for a rails application (redmine): developer.mro.name

 1$ cat /etc/lighttpd/conf-enabled/10-proxy.conf
 2# /usr/share/doc/lighttpd-doc/proxy.txt
 3
 4server.modules += ( "mod_proxy" )
 5
 6# http://blog.mro.name/2013/12/redmine-rails-puma-lighttpd/
 7# http://redmine.lighttpd.net/projects/lighttpd/repository/entry/branches/lighttpd-1.4.x/src/mod_proxy.c?utf8=?&rev=2920#L276
 8$HTTP["host"] == "developer.mro.name" {
 9  $HTTP["url"] !~ "^/\.well-known/acme-challenge/" {
10    proxy.server = ( "" => (("host" => "127.0.0.1", "port" => 8082)))
11    $HTTP["scheme"] == "http" {
12      # capture vhost name with regex condition -> %0 in redirect pattern
13      # must be the most inner block to the redirect rule
14      # zless /usr/share/doc/lighttpd/configuration.txt.gz
15      url.redirect = (".*" => "https://developer.mro.name$0")
16    }
17  }
18}

Ready!

With lighttpd global and vhost configuration set up, config.sh and domains.txt ready, we need just

1$ /var/www/letsencrypt.sh/letsencrypt.sh --cron