it would be great if, as part of the SSL integration, ikiwiki-hosting would call up on the letsencrypt.org gods to install proper certs.
First implementation
so far I have done this by hand, using the main client (previously called letsencrypt, now called certbot, those instructions may need to be updated):
define some handy variables:
domain=example.com email=nobody@$domain username=w-example webroot=/home/$username/public_html/
install the letsencrypt client software:
if you are on Debian sid, stretch or jessie (with backports):
apt install certbot
otherwise, run the magic commands:
git clone https://github.com/certbot/certbot cd certbot ./cerbot-auto --help
request a certificate:
sudo -s [ -d .git ] && source venv/bin/activate # only use virtualenv if checking out from git certbot certonly --webroot \ --agree-tos \ --webroot-path="$webroot" \ --domains $domain --email $email
... notice how you need to change the two last lines for your environment.
this will create a certificate in
/etc/letsencrypt/live/example.com
. install it where ikiwiki expects it:ln -s /etc/letsencrypt/live/$domain/cert.pem /etc/ikiwiki-hosting/config/$username/ssl.crt ln -s /etc/letsencrypt/live/$domain/fullchain.pem /etc/ikiwiki-hosting/config/$username/ssl.chain ln -s /etc/letsencrypt/live/$domain/privkey.pem /etc/ikiwiki-hosting/config/$username/ssl.key
regenerate the apache configs and restart apache:
ikisite enable $domain service apache2 reload # or restart?
Ikiwiki hosting would need to perform steps 3 to 5, and probably have
a Recommends
on certbot. -- anarcat
Potential clients
Finally, note that there is now a fairly large set of Let's Encrypt clients, here's a short list (those have not been reviewed or tested for compatiblity with ikiwiki-hosting):
- dehydrated, bash-only client that should work all Debian releases back to Wheezy (formerly known as letsencrypt.sh, in Debian jessie, stretch and sid)
- simp_le, much simpler implementation in Python (not in Debian)
- acme, root-less, more minimal implementation than the official client, in Go (in Debian stretch and sid)
- lego, root-less, fairly featureful, also in Debian
- acme-tiny is a very small client (~200 lines of python code) that aims to be easily audited and to do only one thing well. for example, you need to generate your RSA key yourself, which may actually be a good thing considering how ikiwiki-hosting works (in Debian stretch and sid)
Note that I have also considered simply using the --apache
plugin to just have LE manage all those certs itself. This could conflict with ikiwiki-hosting, but since it runs fairly frequently, it could just win in the end. It's not a great solution for sites managed by the control panel, but for my use case, it could just work. Unforunately, it parses configs in sites-available instead of just sites-enabled which breaks it for me. --anarcat
Renewals
Also, renewals need to be processed in some way. It seems that it's possible to just run:
letsencrypt -c /etc/letsenctypt/renewal/$domain.conf --renew-by-default
--renew-by-default
renews every time it's run;--keep-until-expiring
instead waits until 30 days before expiry and renews then. I think that's the simpler approach, and the same letsencrypt command line can then both obtain the original cert and renew it. --JoeyTrue, makes a lot more sense! --anarcat
Actually, the
-c /etc/letsenctypt/renewal/$domain.conf
doesn't work at all. The 0.4 client features a newrenew
command that does the right thing (including renewing only if necessary). I tested it here and it seems to work fairly well. My complete update run looks more something like this now: --anarcatletsencrypt renew --non-interactive git -C /etc add -A letsencrypt git commit -m"renew all certs"
So the software name changed: it's certbot now. And yes, the renew command seems to work better. There is a
--post-hook
that can restart apache and do various things with certs, but unfortunately prior to 0.7, it runs all the time. Normally, the--post-hook
is one of those variables that would be saved on renewal, but I haven't tested this because I don't want to have that hook run twice a day. So I'm waiting for this to hit backports before automating this completely, especially since I do not want to diverge from the package's/etc/cron.d/certbot
cronjob. My test renewal line now looks like:certbot --dry-run renew --renew-hook 'service apache2 reload; etckeeper commit "certbot renewed domains: $RENEWED_DOMAINS"'
I'll test this again in a few weeks, hopefully when the new package hits backports. --anarcat
The software has caught up. To avoid customizing the config file, I had to put the renew-hook in the
/etc/letsencrypt/cli.ini
config file, as such:renew-hook = service apache2 reload & etckeeper commit "certbot renewed domains: $RENEWED_DOMAINS"
Notice the
&
instead of;
because the latter breaks the LE config file parser. With that, the configured domains are renewed automatically! To add new domains, I just need to repeat steps 3 to 5 above. Note that extra webroots can be specified with:certbot certonly -w /home/foo -d foo.example.com -w /home/bar -d bar.example.com
I.e. the last webroot path is attached to the next domain specified. Next step is to code this in Perl, I guess? --anarcat
Rate limits
Regarding rate limits, they don't seem to be a problem unless you run a really huge site (500 registrations per 3 hours per IP!). The limits are:
- 100 Names/Certificate (how many domain names you can include in a single certificate)
- 20 Certificates per Domain per week
- 500 Registrations/IP address per 3 hours
- 300 Pending Authorizations/Account per week
That seems to be 5 certificates per domain, not subdomain. Eg, I asked for tmp.kitenet.net 5 times and now it's not allowing getting a cert for downloads.kitenet.net.
So, this seems a pretty big problem; ikiwiki-hosting sites often use subdomains, and if so, quite likely use more than 5. It could keep a site site http-only until it got a cert. But with 100 sites, that would be nearly half a year of delay! (Update: With the new 20 certs/domain/week, that drops to 5 weeks delay.)
And, what about renewal, wouldn't that be rate limited too? (Update: No, it seems that renewals are not rate limited in the 20 certs/domain/week bucket.)
Seems like getting a cert for 100 subdomains under branchable.com (eg), and then moving onto the next cert etc would be the best we could do. But that's a lot more complicated. In particular, renewal needs to use the same set of 100 domains, and if one of the domains is deleted in the meantime, renewal of those 100 probably won't work, and a new certificate would need to be generated for the 99 remaining. --Joey
Ouch, yes, that is a significant problem for branchable!!! Maybe this should be brought up upstream... I wonder if this couldn't be a way to finance letsencrypt, to have larger providers pay a monthly fee for the service, and bypass those limits. --anarcat
WP.com switched to LE, so there's probably a way around those. --anarcat
I asked around and the limits were bumped to 20 certs/domain/week recently. Also, they can tweak the limits upon request. --[[anarcat]
Discussion
Oh, and this could be a plugin, not sure if that would make sense, but it's the way it works on that side of the world. --anarcat
I would much rather use something small enough to be reviewable, particularly if we're going to have to run it unattended as root; the official Let's Encrypt client is huge. We don't have to worry about portability to non-Debian environments or non-Apache web servers, which takes away a lot of the reason to use something so big. --smcv
Kind of agreed, but I'm more interested in ameanability to automation than overall code size. https://github.com/hlandau/acme is one client that seems promising. At the moment only letsencrypt is seems packaged in debian yet and that's the first bar. --Joey
Agreed as well. Just scratching an itch here... The
acme
client looks interesting, but it's go, which brings a whole set of dependencies in. But I like its philosophy and it could integrate well. Also note that the above instructions fail to take into account the canonical and alias names of the site, which breaks openid logins... Something to fix in my future renewals... --anarcat
Here's an interesting puppet implementation that mentions another golang client (lego), which has the interesting property of supporting DNS challenges. The puppet code itself uses the shell script previously mentioned. --anarcat
Note that Let's Encrypt started supporting wildcard certificates recently, but only with DNS challenges.. --anarcat
how to only enable ssl once a certificate is available
Tip from Joey:
Letsencrypt has a bit of a chicken and egg problem, in that the http server has to be up to get the certificate, but the apache site config file has to contain both the http and the https vhost. And the https vhost needs to point to cert files not available until letsencrypt has run.
smcv was thinking about dealing with that by generating a temporary self-signed cert first. But, I don't like the possibility that a http site would come up with such a cert during normal operation.
I found a way around this when adding letsencrypt support to propellor: Make the apache site config file contain only the http vhost, and use IncludeOptional to include another file which contains the https vhost. Generate that file only once letsencrypt has generated the cert.
Note that IncludeOptional has to be used with a wildcard to avoid it being an error if the included file doesn't exist. So, something like:
IncludeOptional /etc/apache2/sites-available/$domain/*.conf
The way I did this here was to generate the cert with the HTTP-only vhost, and only after the cert was generated create the HTTPS vhost... Can't you have a modified HTTPS vhost generated only after the cert is generated? --anarcat
Yes, I think your approach at the top works;
ikisite enable $domain
notices the certs and regenerates an apache config to use them. --Joey
Worked design
Add a use_letsencrypt
config setting in the ikiwiki-hosting plugin.
(done)
Make /etc/ikiwiki-hosting/config/$username/domain.{crt,key,chain} be used for a domain when available, instead of ssl.{crt,key,chain}. This allows urlaliases to each get their own letsencrypt cert. (done)
Add ikisite letsencrypt site
, which first sets use_letsencrypt
and then runs certbot (or whatever, it can be made configurable if
desired) to try to get a certificate for all of the site's domain(s).
If certbot succeeds, it symlinks the cert into place,
and calls ikisite enable site
to update the web server config
to use the cert. (done)
Add ikisite letsnotencrypt site
which undoes that. (done)
Then add a daily cron job that looks for sites that have use_letsencrypt
set, but don't have letsencrypt certs. (Check that the cert files are
symlinks to tell). For each such site, it runs ikisite letsencrypt
to try again to get the cert. (done in ikisite maintaincerts
)
Another cron job can run certbot renew
to handle all renewals.
Seems this doesn't need to rejigger the certificate symlinks or the
apache config for renewed sites, so it should be nice and simple.
(done in ikisite maintaincerts
)
ikisite domains
will need to check use_letsencrypt
. If it's set, and a
new domain is being added to a site, or a domain removed, it needs to
delete the flag file and run ikisite letsencrypt
to (try to) get a new
certificate. What if that fails? It might be possible to keep using the old
letsencrypt certificate in some cases, eg when adding
an alias. Certianly not in the case where the site's primary domain has
changed. The safe option is certainly to remove the old letsencrypt
certificate if it fails, and ikisite enable
will then disable ssl support
for that site, until the cron job later succeeds in getting a certificate.
(done)
Once this is tested and working well, it can be made the default,
by setting use_letsencrypt
in the autosetup/ files. For that to work,
every ikisite command that creates a site (including branching,
restoring from backup, etc) should run ikisite letsencrypt
at the end
when the site has use_letsencrypt
configured. It can take a minute or so
for letsencrypt to run, so best to do that in the background so as to not
slow down site creation etc.
Note: there is already a cron job that runs certbot renew in the Debian package, watch out for dupes... Otherwise: design sounds okay, although I'm not sure why the flag file is necessary. --anarcat
issues
If a site's urlalias includes some domain that is not in the DNS, or no longer points to the host, this is normally not a problem. But, it will prevent certbot getting a cert for that site, because it cannot verify it.
This affects both
ikisite letsencrypt
andikisite domains
.ikisite domains
does not currently ensure that all urlaliases are set in the dns. This check could be added, it might cause some problem though.Note that this is only a problem because the urlaliases are passed to certbot as --host. That is needed because
templates/apache-sitealias.tmpl
includes a https configuration. That lets redirection from the https urlalias to the main url work.But also, if a cert is issued for domains A and B, and B later falls out of the DNS, renewal of the cert will fail. So this can lead to renewal failures later down the line.
Partial fix would be re-running certbot without the urlaliases if it fails with them. Better to have the main url encrypted even if redirection to it cannot be. But, that would leave the apache-sitealias.tmpl generating a config that uses the wrong cert for https, which is bad if some of the urlaliases are accessible in the dns. So, this fix needs a way to prevent that template from using https in this case.
Alternatively, could improve the UI so the site admin can see when letsencrypt failed, and why. (Worth doing in any case, but I'm not sure where to put it in the UI.)
Or, could make apache-sitealias.tmpl use per-domain cert files. This avoids all this complexity. Only the Registration/IP rate limit might be a problem with doing this, but it's sufficiently high. (done)
Sites with a apache.conf.tmpl password protecting the site will not be able to use letencrypt, unless it's relaxed to let .well-known be accessed w/o password. This can be fixed by adding this to the end of their apache.conf.tmpl file:
/public_html/.well-known> AuthType None Require all granted
I definitely want ikiwiki-hosting to support letsencrypt.
It's important that any support includes automated certificate renewals. Either by letting the letsencrypt client set things up in a way it can later handle renewal of itself, or by having ikiwiki-hosting take care of it.
Also, there is rate limiting in the current beta (a few domains per IP or something like that), and there may well be rate limiting post-beta. So initial request may fail and a site be left in http-only mode. Probably calls for a cron job to retry any sites that don't yet have a certificate.