Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 03 Apr 2017 08:30:00 +0200
From:      Dave Cottlehuber <dch@skunkwerks.at>
To:        freebsd-questions@freebsd.org
Subject:   Re: letsencrypt configuration
Message-ID:  <1491201000.3329748.932028040.22FE70EC@webmail.messagingengine.com>
In-Reply-To: <CA%2Bg%2BBvjkSifgxOG9bk6qdu2drt1oY_OhTHdOOsbkKDyJJ0oLgQ@mail.gmail.com>
References:  <77a1e8683e3a15cd08986d66807959b2@drenet.net> <CA%2Bg%2BBvjkSifgxOG9bk6qdu2drt1oY_OhTHdOOsbkKDyJJ0oLgQ@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
> On Sat, Apr 1, 2017 at 2:40 AM, Andre Goree <andre@drenet.net> wrote:
> > So how is everyone going about configuring letsencrypt on FreeBSD?  It would
> > seem that multiple ports that used to exist for this very purpose are no
> > longer in the repos (letskencrypt, py-letsencrypt), so tutorials I'm finding
> > (and even letskencrypt, which is still in the FreeBDS wiki) aren't much
> > help.

I speculate that the letsencrypt trademark has been enforced
https://letsencrypt.org/trademarks/ so people needed to rename their
tools.

I've used a few of them and settled on security/acme-client with a very
simple config that is easy to manage with my config management setup.
Below is a complete example, using www/h2o web server,
/etc/periodic.conf to keep the script running, and security/acme-client
for the heavy lifting.  If there's any info missing let me know, I'll
blog this later in the week. 

```
# /etc/periodic.conf
weekly_acme_client_enable="YES"
weekly_acme_client_renewscript="/usr/local/etc/letsencrypt/example.sh"
```

```
#  /usr/local/etc/letsencrypt/example.sh 
#!/bin/sh -e
mkdir -p /usr/local/etc/ssl/acme_client/host.example.com \
  /usr/local/etc/acme_client/host.example.com \
  /usr/local/etc/ssl/acme_client/private/host.example.com
# add `-s` flag below to use the staging server until you have it
working
# remember to remove all certs and keys when you switch from staging to
prod
# add `-F` flag below to force renewal e.g. if you add another altName
# remove /usr/local/etc/ssl/acme_client/host.example.com/cert.pem
# so that the certificates are regenerated correctly

/usr/local/bin/acme-client -mnN -v \
  -F \
  -C /usr/local/www/acme_client \
  host.example.com \
  altname1.example.com \
  altname2.example.com \
  2>&1 | tee -a /var/log/acme.log

# the public certification chain is now at:
# /usr/local/etc/ssl/acme_client/host.example.com/fullchain.pem
# the private certificate key is now at:
# /usr/local/etc/ssl/acme_client/private/host.example.com/privkey.pem
# make a combined key for haproxy and friends
cat /usr/local/etc/ssl/acme_client/private/host.example.com/privkey.pem
\
    /usr/local/etc/ssl/acme_client/host.example.com/fullchain.pem \
  > /usr/local/etc/ssl/acme_client/host.example.com/combined.pem

chmod 0600 /var/log/acme.log
/usr/local/etc/ssl/acme_client/host.example.com/combined.pem

service h2o restart
service haproxy restart
```

Obviously this needs HTTP:80 support in your web server, I use www/h2o
for this:

```
# /usr/local/etc/h2o/h2o.conf
# vi: ft=yaml
# see https://h2o.examp1e.net/ for detailed documentation
# see h2o --help for command-line options and settings
user: www
pid-file: /var/run/h2o.pid
access-log: /var/log/h2o/h2o-access.log
error-log: /var/log/h2o/h2o-error.log
listen: 80
listen:
  port: 443
  ssl:
    minimum-version: TLSv1.2
    certificate-file:
    /usr/local/etc/ssl/acme/host.example.com/fullchain.pem
    key-file:
    /usr/local/etc/ssl/acme/private/host.example.com/privkey.pem
    cipher-preference: server
    cipher-suite:
    ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS

file.dirlisting: on
file.send-gzip: on
http1-request-timeout: 10
http2-idle-timeout: 10
limit-request-body: 1024
max-connections: 1024

file.mime.addtypes:
  image/svg+xml: .svg
  text/plain: .log
  text/css: .css
  application/atom+xml: .xml
  application/zip: .zip
  application/json: .json
  "text/html; charset=utf-8": .html

# host headers, global
header.add: "x-frame-options: deny"
header.add: "X-XSS-Protection: 1; mode=block"
header.add: "X-Content-Type-Options: nosniff"
header.add: "X-UA-Compatible: IE=Edge"
# 1 month HSTS pinning
header.add: "Strict-Transport-Security: max-age=2628000"
header.add: "Cache-Control: no-transform"

# per-host configuration
hosts:
  host.example.com:
    paths:
      "/":
        mruby.handler: |
          require "htpasswd.rb"
          Htpasswd.new("/usr/local/etc/h2o/private/.htpasswd",
          "example")
        file.dir: "/var/www/host.example.com/"
      "/.well-known/acme-challenge":
        file.dir: "/var/www/acme"

  altname1.example.com:
    paths:
      "/.well-known/acme-challenge":
        file.dir: "/var/www/acme"
      "/":
        file.dir: "/var/www/altname1.example.com"

  altname2.example.com:
    paths:
      "/.well-known/acme-challenge":
        file.dir: "/var/www/acme"
      "/":
        file.dir: "/var/www/altname2.example.com"
```

Some notes to help you:

- you *need* to have port 80 open for http requests for the acme
protocol to do its verification on
- you could add a 301 for anything not in /.well-known/acme-challenge/
- in h2o.conf I don't force use of HTTPS anywhere, but the use of HSTS
will keep browsers that use https once, to use it in future
- the same /var/www/acme dir is re-used for each virtual host
- when you're getting started, use the -S flag in the script to get
dummy certs from the server without using up your acme "budget"
- when you have it all working, delete *all* the generated certs and
scripts before switching back to normal mode (without -S) 
- its possible to use the return code from acme-client to decide whether
to restart the daemons or not
- the simplest solution for me was to restart all the daemons every week
anyway
- the only file that actually matters is your "account key" for each
server, stored in /usr/local/etc/acme/host.example.com/privkey.pem as
all the rest will be regenerated automatically on the next acme-client
run
- if you have multiple servers or services that share the certificate,
you may need to use a reverse proxy to direct things to the appropriate
place so that letsencrypt can find them

A+
Dave



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1491201000.3329748.932028040.22FE70EC>