0

Background

I have an old Play Framework webapp that serves assets from two CDNs (siteassets and courseassets), which are implemented with AWS CloudFront. Pound runs on the same VM as the webapp, and merely acts as an SSL endpoint because the old version Play Framework used does not handle SSL. Pound binds to port 80 and 443. The Play webapp binds to port 9000.

Previously I used a Symantec SSL wildcard certificate for the CDN assets and the webapp. Now that Symantec SSL certs have only a few days left before they are no longer valid I have decided to use AWS Certificate Manager to provision single-domain SSL certificates for the CDNs, and to use Letsencrypt to provide a single-domain SSL certificate for the webapp. The Letsencrypt certificates only last 90 days, so they need to be automatically refreshed.

Pound wants SSL certificates in PEM format, but Letsencrypt does not seem to have an convenient way of providing a PEM format. Letsencrypt does not have any special provision for Play Framework in the way that it does for Apache httpd, Tomcat or other well-known frameworks.

I do not want to take the webapp offline in order to update the SSL certificate used by Pound.

Current Pound Configuration

Following is my current /etc/pound/pound.cfg for Pound version 2.7f-0ubuntu1. This configuration is a few years old and will probably need to be updated, and Pound should probably be updated too.

# Global options

User    "root"
Group   "root"

# Logging: (goes to syslog by default)
#  0  no logging
#  1  normal
#  2  extended
#  3  Apache-style (common log format)
#  4 (same as 3 but without the virtual host information)
#  5 (same as 4 but with information about the Service and BackEnd used)
LogLevel 5

# Check backend every X secs:
Alive    30

# Use hardware-accelleration card supported by openssl(1):
#SSLEngine  "<hw>"

# poundctl control socket
Control "/var/run/poundctl.socket"

# Redirect all http requests on port 80 to https on port 443
# The Play Framework webapp never sees these redirected requests because Pound handles them
ListenHTTP
  Address 0.0.0.0
  Port 80
  Err500 "/usr/local/etc/pound_error_500"
  Err503 "/usr/local/etc/pound_error_500"
  Service
    Redirect 301 "https://www.scalacourses.com"
  End
End

# Redirect all requests on port 443 to the Play Framework webapp on port 9443
ListenHTTPS
  Address 0.0.0.0
  Port 443
  Err500 "/usr/local/etc/pound_error_500"
  Err503 "/usr/local/etc/pound_error_500"
  Cert "/var/work/training/cadenza/conf/ssl/scalacourses.com.pound.pem"
  Disable SSLv3
  Ciphers "EECDH+ECDSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!eNULL:!LOW:!aNULL:!MD5:!DSS"
  SSLAllowClientRenegotiation     0
  SSLHonorCipherOrder 1
  HeadRemove "X-Forwarded-Proto"
  HeadRemove "x-forwarded-proto"
  AddHeader "x-forwarded-proto: https"
  Service
    BackEnd
      #HTTPS
      Address 127.0.0.1
      Port 9000
    End
  End
End

Questions

  1. The certbot ACME client version 0.17.0 was installed via: sudo apt install certbot

    I see that certbot version 0.21.0 is available. Should I use this newer version instead of the default provided by Ubuntu 17.10?

  2. Is there a reasonable version of Pound available in a Debian PPA? Previously I had to build Pound myself, but I would prefer to use a prebuilt version. https://launchpad.net/ubuntu/+source/pound has version 2.7-1.3 ... is this the best stable version?
  3. What changes are required to the Pound configuration in /etc/pound/pound.cfg? Clearly this line will need to be updated: Cert "/var/work/training/cadenza/conf/ssl/scalacourses.com.pound.pem"

    This line may need to be updated when Pound is updated: Ciphers "EECDH+ECDSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!eNULL:!LOW:!aNULL:!MD5:!DSS"

    What other lines might need to be added, deleted or modified? I know Pound should not run as root, but that can be dealt with separately.

  4. Need a script to initially provision the Letsencrypt SSL certificate for Pound. Seems that the certbot program used by Letsencrypt wants to bind to port 80, but Pound binds to this port, and I do not want to take the webapp down in order to upgrade the SSL certificate. Is there a way to run it on the same VM as Pound, or should I run it on another VM and copy over the generated certificate in PEM format when ready?

  5. Need a script that can be invoked via cron every 45 days in order to refresh the SSL certificates.

Mike Slinn
  • 121
  • 5

2 Answers2

0

Set up a small httpd (Nginx, Apache...) that is listen on localhost (127.0.0.1:80) and make the directory [DOCUMENT_ROOT]/.well-known/acme-challenge/ writable for the user that is running this httpd. Than in the pound config create

Service "letsencrypt"
    HeadRequire "Host: .*"
    URL "/.well-known/acme-challenge/.*"
    BackEnd
        Address 127.0.0.1
        Port 80
    End
End

prior to all other rules. Now you should be able to do something like

certbot-auto certonly --email support@example.com -d www.example.com -d example.com --verbose --agree-tos --webroot --webroot-path /var/www/localhost/htdocs/

Please change "/var/www/localhost" to the Document_root of your local httpd. You only have to do this once. And later on you can do something like

# add --dry-run to test it certbot-auto renew --post-hook "/etc/init.d/pound restart" for i in ls /etc/letsencrypt/live/; do cp /etc/letsencrypt/live/$i/privkey.pem /opt/pound_certs/$i.pem ; cat /etc/letsencrypt/live/$i/fullchain.pem >> /opt/pound_certs/$i.pem ; done

And of course dont forget to add the pem file in the pound config later on ;-) I know that the post-hook comes to late. However Letsencrypt will issue new certs 10 days before the old expire. So if do you let do it a cronjob every day this is no problem at all.

ritze
  • 1
  • 2
  • The httpd listener you suggest would have to run on the same machine that Pound runs on, and unfortunately that would require taking down Pound, which as I said in the problem statement is not ok. A better approach would be to use a DNS challenge. – Mike Slinn Feb 21 '18 at 19:23
  • Why do you want shut down pount? As long as pount does not listen on the IP you can use it for the httpd. If 127.0.0.1:80 does not work... use 127.0.0:2:80 or any other free IP or port. – ritze Feb 22 '18 at 05:02
  • Seems you have likely never used Pound. The configuration file shows that it listens on ports 80 and 443, and I also mention that in the problem statement. Also, letsencrypt uses port 80 for the challenge. Your approach cannot work for this scenario. – Mike Slinn Feb 22 '18 at 16:50
  • Sure it is a problem to use :80 and/or :443 if do you bind pound to 0.0.0.0 and not to an individual IP. What you should do if do you plan to use pound in bigger environments. However therefor I have written "any other free IP or port" I dont know your machine but something like 127.0.0.1:666 (TCP) should be free to use with this. It is up to you. – ritze Feb 23 '18 at 07:27
  • As I said in the problem statement, the Letsencrypt HTTP challenge only uses ports 80 or 443. This problem was solved via DNS challenge, and an HTTP challenge is impossible for this scenario. – Mike Slinn Feb 23 '18 at 18:32
  • This solution solves a problem that _does_not_exists_!!! Configure the Pound software as suggested and let the _backend_ webserver listen on any free TCP port. Thats it. Of course you can create much more difficult solutions. The DNS challenge you found is one of this. And you can create much more new problems with it like use of only one certificate for all domains that Pound serves... Perhaps you will extend your scripts... The DNS challange is fine if do you want create a generic solution for different load balancers. But you asked for a solution with the Pound balancer software. – ritze Feb 25 '18 at 00:34
  • Ritze, I am going to ignore you from now on. – Mike Slinn Feb 25 '18 at 04:16
0
  1. The current version of certbot works fine, no need for the latest.
  2. Still do not know about a prebuilt version of Pound from recent sources.
  3. I only had to change the Cert line, which makes sense because I have not (yet) upgraded Pound.
  4. The script can run on any VM because it does not bind to port 80 or port 443 when a DNS challenge is used.

Following is certbot53, a script I wrote to use jed/certbot-route53 for authentication via DNS challenge. This script relies on a PR that I submitted to certbot-route53 that fixes two bugs.

#!/bin/bash

#STAGING=--staging
CERT_DIR=/etc/pound/certbot
DOMAIN=scalacourses.com
MAIL_ADDR='mslinn@scalacourses.com'
SCRIPT_NAME=certbot-route53.sh

if [ ! -d "$CERT_DIR/letsencrypt" ]; then sudo mkdir -p $CERT_DIR/letsencrypt; fi
sudo chmod 777 "$CERT_DIR/letsencrypt"

cd $CERT_DIR

if [ ! -f "$CERT_DIR/$SCRIPT_NAME" ]; then
  sudo curl -sL https://git.io/vylLx -o $SCRIPT_NAME
  sudo chmod a+x certbot-route53.sh
fi

./$SCRIPT_NAME \
  --agree-tos \
  --manual-public-ip-logging-ok \
  --domains $DOMAIN,www.$DOMAIN \
  --renew-by-default \
  --email $MAIL_ADDR $STAGING

PRIV_KEY="$CERT_DIR/letsencrypt/live/$DOMAIN/privkey.pem"
FULL_CHAIN="$CERT_DIR/letsencrypt/live/$DOMAIN/fullchain.pem"
COMBINED="$CERT_DIR/combined-for-pound.pem"
cat "$PRIV_KEY" "$FULL_CHAIN" | sudo tee "$COMBINED" > /dev/null
  1. Currently I am rerunning the same script via a cron job. This means I am not upgrading certificates, instead I am replacing them. Unclear what the benefit of upgrading a certificate might be. Here is the cron entry, which runs the script as my userid every 2 months; I entered this after typing sudo crontab -e:

    0 0 1 */2 * su mslinn /usr/local/bin/certbot53
    
Mike Slinn
  • 121
  • 5