Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. Started this as I felt that the standard [[https://letsencrypt.org|LetsEncrypt]] client was way too fat and had too many dependencies to be allowed to run as root. Even though this is all pretty basic stuff, I decided to document it here. <callout type="info"> **Guide changed to use the ''%%security/acme-client%%''** The guide using official %%LetsEncrypt%% client can be found at [[BernardSpil/LetsEncrypt.py]]\\ The guide using the %%LetsEncrypt%%.sh client can be found at [[BernardSpil/LetsEncrypt.sh]] </callout> My [[BernardSpil/LetsEncrypt.py|first guide]] used the official %%LetsEncrypt%% python client. I found that to be way too fat and had too many dependencies to be allowed to run as root.\\ My [[https://https://brnrd.eu/security/2016-01-23/letsencrypt.html|second guide]] used Lukas Schauer's %%LetsEncrypt%%.sh client which only required ''%%openssl%%'' and either ''%%bash%%'' or ''%%zsh%%''. This is still a good method as it has separated privileged and un-privileged actions. This latest guide uses [[https://kristaps.bsd.lv/acme-client/|acme-client]] created by Kristaps Dzonsons. acme-client is a client for Let's Encrypt users, but one designed for **security**. No Python. No Ruby. No Bash. A straightforward, open source implementation in C that isolates each step of the sequence. As a proponent of %%LibreSSL%% I can't let solutions that use libtls from %%LibreSSL%% pass by without trying to use them. I'm the creator and maintainer of the [[http://www.freshports.org/security/acme-client/|security/acme-client]] port in the %%FreeBSD%% ports tree. ====== Notes ====== Some notes on the configuration of my setup - All services accessible from the internet run in jails (all jails reside in ''%%/usr/jails%%'' by default on %%FreeBSD%%) - I use [[https://www.LibreSSL.org|LibreSSL]] as the provider of libcrypto, libssl and libtls on my %%FreeBSD%% system. The ''%%security/acme-client%%'' port depends on %%LibreSSL%%. The ''%%acme-client%%'' process will be started by root but drops privileges to [[https://en.wikipedia.org/wiki/Nobody_(username)|nobody]] and [[https://en.wikipedia.org/wiki/Chroot|chroot]]s any action that does not require root privileges. It must run as ''%%root%%'' to be able to drop privileges and run as an unprivileged user. ====== Install Letsencrypt.sh ====== The port is available in the ports tree. Install it using the official pkg repository using <code> pkg install letsencrypt </code> or alternatively build your own using [Poudriere%%](%%[[https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-poudriere.html]]) or any of the other building-from-source options and install it. The port works with either ''%%security/libressl%%'' or ''%%security/libressl-devel%%''. If you want to use the newer 2.4 branch of %%LibreSSL%% you should add to ''%%/etc/make.conf%%'' <code> DEFAULT_VERSIONS+= ssl:libressl-devel </code> The %%FreeBSD%% ports framework will detect that an %%OpenSSL%%/%%LibreSSL%% port is installed and then default to depend on the port rather than base. This will hit you if you use ''%%portmaster%%''/''%%portupdate%%'' or generally build 'in-situ'. You are encouraged to build using ''%%poudriere%%'' to avoid this. Configuration will land in ''%%/usr/local/etc/letsencrypt%%''. The keys, certificates and certificate-chains will be stored in ''%%/usr/local/etc/ssl/letsencrypt%%'' by default. You should want to check that the configuration directory is not world-writable. The default directories in /usr/local/etc/ssl will be created with sane access restrictions when you install the port or package. <code> /usr/local/etc/ letsencrypt/ ssl/ ssl/letsencrypt/ ssl/letsencrypt/private/ </code> ====== Prepare directories ====== To make life easier all of the challenges (%%LetsEncrypt%% as well as keybase etc) will be hosted in a shared dir ''%%/usr/local/www/.well-known%%'' on the jail running my Apache server. <code> mkdir -pm750 /usr/jails/http/usr/local/www/.well-known </code> The %%LetsEncrypt%% and acme-client bits will land in ''%%/usr/local/etc/letsencrypt%%'', the private keys will land in ''%%/usr/local/etc/ssl/letsencrypt/private%%'' and certificates will land in domain-specific directories in ''%%/usr/local/etc/ssl/letsencrypt%%'' on the host system. These directories are created by the port/package upon installation apart from the domain-specific certificate directories. ===== Migration from LetsEncrypt.sh ===== To migrate from the ''%%LetsEncrypt.sh%%'' method, copy/move your account key, the ''%%domains.txt%%'' file and all keys and certs to the new locations. Use the default filename, resolve symlinks to the actual timestamped file. Use the script as an example, yet it //should// work for the default settings. ''%%migrate.sh%%'' <code> OLDDIR="/usr/local/etc/letsencrypt.sh" NEWDIR="/usr/local/etc/ssl/letsencrypt" cp -p /usr/local/etc/letsencrypt{.sh,}/domains.txt} cp -p /usr/local/etc/letsencrypt{.sh/private_key.pem,/privkey.pem} cat "${OLDDIR}/domains.txt" | while read domain line ; do mkdir -pm755 "${NEWDIR}/${domain}" cp -L "${OLDDIR}/certs/${domain}/privkey.pem" \ "${NEWDIR}/private/${domain}.pem" cp -L "${OLDDIR}/certs/${domain}/{cert,chain,fullchain}.pem" \ "${NEWDIR}/${domain}/" done </code> ===== Modify Apache configuration ===== The acme validation will ''%%GET%%'' a uniquely named file from ''%%http://<example.org>/.well-known/acme-challenge/%%'' Access to the ''%%.well-known%%'' directory is granted in my main Apache config file ''%%/usr/local/etc/apache24/httpd.conf%%'' ''%%httpd.conf%%'' <code> <Directory "/usr/local/www/.well-known/"> Options None AllowOverride None Require all granted Header add Content-Type text/plain </Directory> </code> The Content-Type header was in my configs somewhere, shouldn't hurt.\\ If you want to only share the ACME challenges you can suffix ''%%.well-known/%%'' with ''%%acme-challenge/%%''. Now every (non-ssl) Virtual Host that I have gets a on-line addition ''%%vhosts/domain.conf%%'' <code> Alias /.well-known/ /usr/local/www/.well-known/ </code> <callout type="warning"> You need to make sure that all (sub-)domains that you want to sign have access to this directory! That includes rewrites etc. The acme validation is done only using **plain http** and will not honour redirects etc. </callout> ===== acme-client configuration ===== acme-client works differently from the other clients I've used as it does **not** use configuration files. Everything is handled passing parameters with values to the command. The intended use-case is a system that hosts a single domain. I've tried to remain compatible with the ''%%LetsEncrypt.sh%%'' method using a file that lists domains and using the first (primary) hostname as prefix for the file and directory names. A filename-prefix handling is likely to be added in a future version. This could equally well be done with an inline HERE-document as documented below. ===== Domains to sign ===== The script requires a list of domain names you want to have a SAN cert for in the following format: <code> example.com www.example.com example.net www.example.net wiki.example.net </code> Domains and sub-domains that are listed on the //same line// will result in SAN-certificates ([[https://en.wikipedia.org/wiki/SubjectAltName|Subject-Alternative-Name]]).\\ Store this as ''%%/usr/local/etc/letsencrypt/domains.txt%%'' <callout type="warning"> Make sure the first item in every line of ''%%domains.txt%%'' is unique or you'll end up in a real mess! </callout> ===== In-line configuration ===== If you don't want to use a ''%%domains.txt%%'' configuration file you can use a different construct to include the list in your ''%%/usr/local/etc/letsencrypt/acme-client.sh%%'' script (changed lines only). <code> ... while read domain line ; do ... done <<ENDOFLIST example.com www.example.com example.net www.example.net wiki.example.net ENDOFLIST </code> ===== Configure periodic job ===== The %%FreeBSD%% port contains a [[https://www.freebsd.org/cgi/man.cgi?query=periodic|periodic(8)]] script for full automation of your certificate renewal. The periodic script allows using a script for renewals or periodic variables only for a single key/certifcate To setup periodic to use the script ''%%/etc/periodic.conf%%'' <code> weekly_acme-client_enable="YES" weekly_acme-client_renewscript="/usr/local/etc/letsencrypt/acme-client.sh" weekly_acme-client_deployscript="/usr/local/etc/letsencrypt/deploy.sh" </code> Obviously you can also add your deployment to the renewal script if you would like to. If you have only one certificate to renew on the machine, then you do so without a script by using periodic variables ''%%/etc/periodic.conf%%'' <code> weekly_acme-client_enable="YES" weekly_acme-client_domains="example.com www.example.com example.net www.example.net" weekly_acme-client_challengedir="/usr/jails/http/usr/local/www/.well-known/acme-challenge" weekly_acme-client_args="-c /usr/jails/http/usr/local/ssl/certs -p /usr/jails/http/usr/local/ssl/priv" </code> In stead of using the ''%%weekly_acme-client_args%%'' you can also use ''%%weekly_acme-client_deployscript%%'' for your single certificate deployment. The remainder of this guide assumes you use the ''%%weekly_acme-client_renewscript%%'' method. ====== First run ====== You will probably want to run your %%LetsEncrypt%% manually the first time (as ''%%root%%'') after you've setup periodic <code> /usr/local/etc/periodic/weekly/000.acme-client.sh </code> You will end up with a sub-directory ''%%certs%%'' that contains your domains as directories with the Subject-Alternative-Names certs and the corresponding private keys in the ''%%private%%'' sub-directory. <code> /usr/local/etc/ssl/ certs/example.com/ cert.pem chain.pem fullchain.pem private/example.com.pem certs/example.net/ cert.pem chain.pem fullchain.pem priv/example.net.pem </code> ====== Deploy new certs ====== The port contains a script (''%%/usr/local/etc/letsencrypt/deploy.sh%%'') that you can adapt to your needs. Here you'll probably need to get creative with scripting. In the host environment, your now have <code> /usr/local/etc/ssl/letsencrypt/private/example.net.pem /usr/local/etc/ssl/letsencrypt/certs/example.net/fullchain.pem </code> ===== Example (jailed) applications ===== Your Apache server may (should?) run in the ''%%http%%'' jail and you've setup an Apache Virtual Host with <code> SSLCertificateFile /etc/ssl/certs/example.net.pem SSLCertificateKeyFile /etc/ssl/priv/example.net.pem </code> and your %%OpenSMTPd%% mailserver for example.net in the ''%%mail%%'' jail <code> pki example.net certificate "/etc/ssl/certs/example.net.pem" pki example.net key "/etc/ssl/priv/example.net.pem" listen on $lan_addr port 587 tls-require \ pki example.net hostname example.net auth </code> Seen from the host environment your certificates actually need to end up in <code> /usr/jails/http/etc/ssl /usr/jails/mail/etc/ssl </code> **NB:** Some applications want a private key, certificate and separate chain instead. If this is the case you'll need to copy ''%%cert.pem%%'' and ''%%chain.pem%%'' to the appropriate location instead. ---- {{tag>HowTo}} bernardspil/letsencrypt.txt Last modified: 2019/01/02 18:47by JuanMolina