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

$ 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