From owner-freebsd-questions@freebsd.org Mon Apr 3 06:30:02 2017 Return-Path: Delivered-To: freebsd-questions@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 2FB8BD2B2A2 for ; Mon, 3 Apr 2017 06:30:02 +0000 (UTC) (envelope-from dch@skunkwerks.at) Received: from out1-smtp.messagingengine.com (out1-smtp.messagingengine.com [66.111.4.25]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id EE064964 for ; Mon, 3 Apr 2017 06:30:01 +0000 (UTC) (envelope-from dch@skunkwerks.at) Received: from compute7.internal (compute7.nyi.internal [10.202.2.47]) by mailout.nyi.internal (Postfix) with ESMTP id 3B5F320970 for ; Mon, 3 Apr 2017 02:30:00 -0400 (EDT) Received: from web6 ([10.202.2.216]) by compute7.internal (MEProxy); Mon, 03 Apr 2017 02:30:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=skunkwerks.at; h=content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to:x-me-sender :x-me-sender:x-sasl-enc; s=mesmtp; bh=tJpsDuE4imB1YSySpUiNRjAuwP TdEQyDXf3ccKPCx4I=; b=fjgvS9AL7ijHOiTPB87rex4zV+5rmHTEmlHBiO5jwB kTRodkdOTC+3JVeyr+vsVwATjGtJHxycwtLA9dph9CJcV4PdMm1jBB8Z2O9AhHXF 16JcNSkqhxC5RWDZm+OgJOp+/DwTkZMlbcWA5XuLPvjD+gWFW1N1XwUuEWvrl679 8= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=content-transfer-encoding:content-type :date:from:in-reply-to:message-id:mime-version:references :subject:to:x-me-sender:x-me-sender:x-sasl-enc; s=fm1; bh=tJpsDu E4imB1YSySpUiNRjAuwPTdEQyDXf3ccKPCx4I=; b=pzgu5hMxvyaiLlUh1mj6I2 lM/JoWDpj9F17E2/HY3ZfmKBu/d/UjZFVH4goZ5dgtpbpsHa8+qJ2INcUaUpUYBl dpsnNVpGeFB24BHKRFMXsXAWMSR7UrG6wshLo4bwMA9S2wVaQ/5v7Sf2rK4pc4cO tFbxP7RqtHEgQyh6KEX6JBdzOjeCUl0+HqMPrr40HD8wDRgZoV25RU2R/Qnrqbfs y04yr+HeKMHHa1T6C0yndW34EiambHQTemXYCR0chM/nsHITCr6FKvoNbaGX9wKP ecBocEVhmzk6qmedWvS7cpNGj912foQhmtDRLv4/5zTyULZuV/mOD6r0OTfSpBqg == X-ME-Sender: Received: by mailuser.nyi.internal (Postfix, from userid 99) id 1931A48004; Mon, 3 Apr 2017 02:30:00 -0400 (EDT) Message-Id: <1491201000.3329748.932028040.22FE70EC@webmail.messagingengine.com> From: Dave Cottlehuber To: freebsd-questions@freebsd.org MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" X-Mailer: MessagingEngine.com Webmail Interface - ajax-6cc8b445 Date: Mon, 03 Apr 2017 08:30:00 +0200 References: <77a1e8683e3a15cd08986d66807959b2@drenet.net> Subject: Re: letsencrypt configuration In-Reply-To: X-BeenThere: freebsd-questions@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: User questions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 03 Apr 2017 06:30:02 -0000 > On Sat, Apr 1, 2017 at 2:40 AM, Andre Goree 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