From 5cb6e5d903eb2001d14e7493aadd7b9635f09021 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 30 Jul 2022 11:36:24 +0100 Subject: add lets encrypt article --- ...022-07-30-lets-encrypt-with-openbsd-and-rex.gmi | 662 ++++++++++++++++++ gemfeed/atom.xml | 769 ++++++++++++++++++--- gemfeed/index.gmi | 1 + 3 files changed, 1351 insertions(+), 81 deletions(-) create mode 100644 gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi (limited to 'gemfeed') diff --git a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi new file mode 100644 index 00000000..2e421ee7 --- /dev/null +++ b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi @@ -0,0 +1,662 @@ +# Let's Encrypt with OpenBSD and Rex + +``` + / _ \ + The Hebern Machine \ ." ". / + ___ / \ + .."" "".. | O | + / \ | | + / \ | | + --------------------------------- + _/ o (O) o _ | + _/ ." ". | + I/ _________________/ \ | + _/I ." | | + ===== / I / / | + ===== | | | \ | _________________." | +===== | | | | | / \ / _|_|__|_|_ __ | + | | | | | | | \ "._." / o o \ ." ". | + | --| --| -| / \ _/ / \ | + \____\____\__| \ ______ | / | | | + -------- --- / | | | + ( ) (O) / \ / | + ----------------------- ".__." | + _|__________________________________________|_ + / \ + /________________________________________________\ + ASCII Art by John Savard +``` + +> Published by Paul at 2022-07-30 + +I was amazed how easy it is to automatically generate and update Let's Encrypt certificates with OpenBSD. + +## What's Let's Encrypt? + +> Let's Encrypt is a non-profit certificate authority run by Internet Security Research Group that provides X.509 certificates for Transport Layer Security (TLS) encryption at no charge. It is the world's largest certificate authority, used by more than 265 million websites, with the goal of all websites being secure and using HTTPS. + +=> https://en.wikipedia.org/wiki/Let's_Encrypt Source: Wikipedia + +In short, it gives away TLS certificates for your website - for free! The catch is, that the certificates are only valid for three months. So it is better to automate certificate generation and renevals. + +## Meet `acme-client` + +`acme-client` is the default Automatic Certifcate Management Environment (ACME) client on OpenBSD and part of the OpenBSD base system. + +When invoked, the client first checks whether certificates actually require to be generated. + +* It first checks whether a certificate already exists, if not, it will attempt to generate a new one. +* If the certificate already exists but expires within the next 30 days, it will renew it. +* Otherwise, `acme-client` won't do anything. + +Oversimplified, the following steps are undertaken by `acme-client` for generating a new certificate: + +* Reading its config file `/etc/acme-client.conf` for a list of hosts (and their alternative names) to generate certificates for. So it means you can also have certificates for abritary subdomains! +* Automatic generation of the private certificate part (the certificate key) and the certificate signing request (CSR) to `/etc/ssl/...`. +* Requesting Let's Encrypt to sign the certificate. This also includes providing a set of temporary files which will be requested by Let's Encrypt in the next step for verification. +* Let's Encrypt then will contact the hostname for the certificate through a special URL (e.g. `http://foo.zone/.well-known/acme-challenge/...`) to verify that the requester is the valid owner of the host. +* Let's Encrypt generates a certificate, which then is downloaded to `/etc/ssl/...`. + +## Configuration + +There is some (but easy) configuration required to make that all work on OpenBSD. + +### acme-client.conf + +This is how my `/etc/acme-client.conf` looks like (I copied a template from `/etc/examples/acme-client.conf` to `/etc/acme-client.conf` and added my domains to the bottom: + +``` +# +# $OpenBSD: acme-client.conf,v 1.4 2020/09/17 09:13:06 florian Exp $ +# +authority letsencrypt { + api url "https://acme-v02.api.letsencrypt.org/directory" + account key "/etc/acme/letsencrypt-privkey.pem" +} + +authority letsencrypt-staging { + api url "https://acme-staging-v02.api.letsencrypt.org/directory" + account key "/etc/acme/letsencrypt-staging-privkey.pem" +} + +authority buypass { + api url "https://api.buypass.com/acme/directory" + account key "/etc/acme/buypass-privkey.pem" + contact "mailto:me@example.com" +} + +authority buypass-test { + api url "https://api.test4.buypass.no/acme/directory" + account key "/etc/acme/buypass-test-privkey.pem" + contact "mailto:me@example.com" +} + +domain buetow.org { + alternative names { www.buetow.org paul.buetow.org } + domain key "/etc/ssl/private/buetow.org.key" + domain full chain certificate "/etc/ssl/buetow.org.fullchain.pem" + sign with letsencrypt +} + +domain dtail.dev { + alternative names { www.dtail.dev } + domain key "/etc/ssl/private/dtail.dev.key" + domain full chain certificate "/etc/ssl/dtail.dev.fullchain.pem" + sign with letsencrypt +} + +domain foo.zone { + alternative names { www.foo.zone } + domain key "/etc/ssl/private/foo.zone.key" + domain full chain certificate "/etc/ssl/foo.zone.fullchain.pem" + sign with letsencrypt +} + +domain irregular.ninja { + alternative names { www.irregular.ninja } + domain key "/etc/ssl/private/irregular.ninja.key" + domain full chain certificate "/etc/ssl/irregular.ninja.fullchain.pem" + sign with letsencrypt +} + +domain snonux.land { + alternative names { www.snonux.land } + domain key "/etc/ssl/private/snonux.land.key" + domain full chain certificate "/etc/ssl/snonux.land.fullchain.pem" + sign with letsencrypt +} +``` + +### httpd.conf + +For ACME to work you will also need to configure the HTTP daemon so that the "special" ACME requests made from Let's Encrypt are served correctly. I am using the standard OpenBSD `httpd` here. These are the snippets I use for the `foo.zone` host in `/etc/httpd.conf` (of course, you need similar setup for all other hosts as well): + +``` +server "foo.zone" { + listen on * port 80 + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + location * { + block return 302 "https://$HTTP_HOST$REQUEST_URI" + } +} + +server "foo.zone" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/foo.zone.fullchain.pem" + key "/etc/ssl/private/foo.zone.key" + } + location * { + root "/htdocs/gemtexter/foo.zone" + directory auto index + } +} +``` + +As you see, plain HTTP only serves the ACME challenge path. Otherwise, it is redirecting the requests to TLS. The TLS section then attempts to use the Let's Encrypt certificates. + +It is worth noticing that `httpd` will start without the certificates actually being present. This will cause a certificate error when you try to reach the HTTPS endpoint but it helps to bootstrap Let's Encrypt. As you saw in the config snippet above, Let's Encrypt only requests the plain HTTP endpoint for the verification process so that HTTPS doesn't need to be operational yet at this stage. But once the certificates are generated you will have to reload or restart `httpd` in order to use any new certificate. + +### CRON job + +You could now run `doas acme-client foo.zone` to generate the certificate or to renew it. Or you could automate it with CRON. + +I have created a script `/usr/local/bin/acme.sh` for that for all of my domains: + +``` +#!/bin/sh + +function handle_cert { + host=$1 + # Create symlink, so that relayd also can read it. + crt_path=/etc/ssl/$host + if [ -e $crt_path.crt ]; then + rm $crt_path.crt + fi + ln -s $crt_path.fullchain.pem $crt_path.crt + # Requesting and renewing certificate. + /usr/sbin/acme-client -v $host +} + +has_update=no +handle_cert www.buetow.org +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.paul.buetow.org +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.tmp.buetow.org +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.dtail.dev +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.foo.zone +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.irregular.ninja +if [ $? -eq 0 ]; then + has_update=yes +fi +handle_cert www.snonux.land +if [ $? -eq 0 ]; then + has_update=yes +fi + +# Pick up the new certs. +if [ $has_update = yes ]; then + /usr/sbin/rcctl reload httpd + /usr/sbin/rcctl reload relayd + /usr/sbin/rcctl restart smtpd +fi +``` + +And added the following line to `/etc/daily.local` to run the script once daily so that certificates will be renewed fully automatically: + +``` +/usr/local/bin/acme.sh +``` + +I am receiving a daily output via E-Mail like this now: + +``` +Running daily.local: +acme-client: /etc/ssl/buetow.org.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/paul.buetow.org.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/tmp.buetow.org.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/dtail.dev.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/foo.zone.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/irregular.ninja.fullchain.pem: certificate valid: 80 days left +acme-client: /etc/ssl/snonux.land.fullchain.pem: certificate valid: 79 days left +``` + +## relayd.conf and smtpd.conf + +Besides of `httpd`, `relayd` (mainly for Gemini) and `smtpd` (for mail, of course) also use TLS certificates. And as you can see in `acme.sh`, the services are also reloaded or restarted (`smtpd` doesn't support reload) whenever a certificate was generated or updated. + +## Rexification + +I didn't write all these configuration files by hand. As a matter of fact, everything is automated with the Rex configuration management system. + +=> https://www.rexify.org + +At the top of the `Rexfile` I define all my hosts: + +``` +our @acme_hosts = qw/buetow.org paul.buetow.org tmp.buetow.org dtail.dev foo.zone irregular.ninja snonux.land/; +``` + +### General ACME client configuration + +ACME will be installed into the frontend group of hosts. Here, blowfish is the primary, and twofish the secondary OpenBSD box. + +``` +group frontends => 'blowfish.buetow.org', 'twofish.buetow.org'; +``` + +This is my Rex task for the general ACME configuration: + +``` +desc 'Configure ACME client'; +task 'acme', group => 'frontends', + sub { + file '/etc/acme-client.conf', + content => template('./etc/acme-client.conf.tpl', + acme_hosts => \@acme_hosts, + is_primary => $is_primary), + owner => 'root', + group => 'wheel', + mode => '644'; + + file '/usr/local/bin/acme.sh', + content => template('./scripts/acme.sh.tpl', + acme_hosts => \@acme_hosts, + is_primary => $is_primary), + owner => 'root', + group => 'wheel', + mode => '744'; + + file '/etc/daily.local', + ensure => 'present', + owner => 'root', + group => 'wheel', + mode => '644'; + + append_if_no_such_line '/etc/daily.local', '/usr/local/bin/acme.sh'; + }; +``` + +And there is also a Rex task just to run the ACME script remotely: + +``` +desc 'Invoke ACME client'; +task 'acme_invoke', group => 'frontends', + sub { + say run '/usr/local/bin/acme.sh'; + }; + +``` + +Furthermore, this snippet (also at the top of the Rexfile) helps to determine whether the current server is the primary server (all hosts will be without the `www.` prefix) or the secondary server (all hosts will be with the `www.` prefix): + +``` +# Bootstrapping the FQDN based on the server IP as the hostname and domain +# facts aren't set yet due to the myname file in the first place. +our $fqdns = sub { + my $ipv4 = shift; + return 'blowfish.buetow.org' if $ipv4 eq '23.88.35.144'; + return 'twofish.buetow.org' if $ipv4 eq '108.160.134.135'; + Rex::Logger::info("Unable to determine hostname for $ipv4", 'error'); + return 'HOSTNAME-UNKNOWN.buetow.org'; +}; + +# To determine whether the server is the primary or the secondary. +our $is_primary = sub { + my $ipv4 = shift; + $fqdns->($ipv4) eq 'blowfish.buetow.org'; +}; +``` + +The following is the `acme-client.conf.tpl` Rex template file used for the automation. You see here that the `www.` prefix isn't sent for the primary server. E.g. `foo.zone` will be served by the primary server (in my case a server located in Germany) and `www.foo.zone` by the secondary server (in my case a server located in Japan): + +``` +# +# $OpenBSD: acme-client.conf,v 1.4 2020/09/17 09:13:06 florian Exp $ +# +authority letsencrypt { + api url "https://acme-v02.api.letsencrypt.org/directory" + account key "/etc/acme/letsencrypt-privkey.pem" +} + +authority letsencrypt-staging { + api url "https://acme-staging-v02.api.letsencrypt.org/directory" + account key "/etc/acme/letsencrypt-staging-privkey.pem" +} + +authority buypass { + api url "https://api.buypass.com/acme/directory" + account key "/etc/acme/buypass-privkey.pem" + contact "mailto:me@example.com" +} + +authority buypass-test { + api url "https://api.test4.buypass.no/acme/directory" + account key "/etc/acme/buypass-test-privkey.pem" + contact "mailto:me@example.com" +} + +<% + our $primary = $is_primary->($vio0_ip); + our $prefix = $primary ? '' : 'www.'; +%> + +<% for my $host (@$acme_hosts) { %> +domain <%= $prefix.$host %> { + domain key "/etc/ssl/private/<%= $prefix.$host %>.key" + domain full chain certificate "/etc/ssl/<%= $prefix.$host %>.fullchain.pem" + sign with letsencrypt +} +<% } %> + +``` + +And this is the `acme.sh.tpl`: + +``` +#!/bin/sh + +<% + our $primary = $is_primary->($vio0_ip); + our $prefix = $primary ? '' : 'www.'; +-%> + +function handle_cert { + host=$1 + # Create symlink, so that relayd also can read it. + crt_path=/etc/ssl/$host + if [ -e $crt_path.crt ]; then + rm $crt_path.crt + fi + ln -s $crt_path.fullchain.pem $crt_path.crt + # Requesting and renewing certificate. + /usr/sbin/acme-client -v $host +} + +has_update=no +<% for my $host (@$acme_hosts) { -%> +handle_cert <%= $prefix.$host %> +if [ $? -eq 0 ]; then + has_update=yes +fi +<% } -%> + +# Pick up the new certs. +if [ $has_update = yes ]; then + /usr/sbin/rcctl reload httpd + /usr/sbin/rcctl reload relayd + /usr/sbin/rcctl restart smtpd +fi +``` + +### Service rexification + +These are the Rex tasks setting up `httpd`, `relayd` and `smtpd` services: + +``` +desc 'Setup httpd'; +task 'httpd', group => 'frontends', + sub { + append_if_no_such_line '/etc/rc.conf.local', 'httpd_flags='; + + file '/etc/httpd.conf', + content => template('./etc/httpd.conf.tpl', + acme_hosts => \@acme_hosts, + is_primary => $is_primary), + owner => 'root', + group => 'wheel', + mode => '644', + on_change => sub { service 'httpd' => 'restart' }; + + service 'httpd', ensure => 'started'; + }; + +desc 'Setup relayd'; +task 'relayd', group => 'frontends', + sub { + append_if_no_such_line '/etc/rc.conf.local', 'relayd_flags='; + + file '/etc/relayd.conf', + content => template('./etc/relayd.conf.tpl', + ipv6address => $ipv6address, + is_primary => $is_primary), + owner => 'root', + group => 'wheel', + mode => '600', + on_change => sub { service 'relayd' => 'restart' }; + + service 'relayd', ensure => 'started'; + }; + +desc 'Setup OpenSMTPD'; +task 'smtpd', group => 'frontends', + sub { + Rex::Logger::info('Dealing with mail aliases'); + file '/etc/mail/aliases', + source => './etc/mail/aliases', + owner => 'root', + group => 'wheel', + mode => '644', + on_change => sub { say run 'newaliases' }; + + Rex::Logger::info('Dealing with mail virtual domains'); + file '/etc/mail/virtualdomains', + source => './etc/mail/virtualdomains', + owner => 'root', + group => 'wheel', + mode => '644', + on_change => sub { service 'smtpd' => 'restart' }; + + Rex::Logger::info('Dealing with mail virtual users'); + file '/etc/mail/virtualusers', + source => './etc/mail/virtualusers', + owner => 'root', + group => 'wheel', + mode => '644', + on_change => sub { service 'smtpd' => 'restart' }; + + Rex::Logger::info('Dealing with smtpd.conf'); + file '/etc/mail/smtpd.conf', + content => template('./etc/mail/smtpd.conf.tpl', + is_primary => $is_primary), + owner => 'root', + group => 'wheel', + mode => '644', + on_change => sub { service 'smtpd' => 'restart' }; + + service 'smtpd', ensure => 'started'; + }; + +``` + +This is `httpd.conf.tpl`: + +``` +<% + our $primary = $is_primary->($vio0_ip); + our $prefix = $primary ? '' : 'www.'; +%> + +# Plain HTTP for ACME and HTTPS redirect +<% for my $host (@$acme_hosts) { %> +server "<%= $prefix.$host %>" { + listen on * port 80 + location "/.well-known/acme-challenge/*" { + root "/acme" + request strip 2 + } + location * { + block return 302 "https://$HTTP_HOST$REQUEST_URI" + } +} +<% } %> + +# Gemtexter hosts +<% for my $host (qw/foo.zone snonux.land/) { %> +server "<%= $prefix.$host %>" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix.$host %>.fullchain.pem" + key "/etc/ssl/private/<%= $prefix.$host %>.key" + } + location * { + root "/htdocs/gemtexter/<%= $host %>" + directory auto index + } +} +<% } %> + +# DTail special host +server "<%= $prefix %>dtail.dev" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix %>dtail.dev.fullchain.pem" + key "/etc/ssl/private/<%= $prefix %>dtail.dev.key" + } + location * { + block return 302 "https://github.dtail.dev$REQUEST_URI" + } +} + +# Irregular Ninja special host +server "<%= $prefix %>irregular.ninja" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix %>irregular.ninja.fullchain.pem" + key "/etc/ssl/private/<%= $prefix %>irregular.ninja.key" + } + location * { + root "/htdocs/irregular.ninja" + directory auto index + } +} + +# buetow.org special host. +server "<%= $prefix %>buetow.org" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix %>buetow.org.fullchain.pem" + key "/etc/ssl/private/<%= $prefix %>buetow.org.key" + } + block return 302 "https://paul.buetow.org" +} + +server "<%= $prefix %>paul.buetow.org" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix %>paul.buetow.org.fullchain.pem" + key "/etc/ssl/private/<%= $prefix %>paul.buetow.org.key" + } + block return 302 "https://foo.zone/contact-information.html" +} + +server "<%= $prefix %>tmp.buetow.org" { + listen on * tls port 443 + tls { + certificate "/etc/ssl/<%= $prefix %>tmp.buetow.org.fullchain.pem" + key "/etc/ssl/private/<%= $prefix %>tmp.buetow.org.key" + } + root "/htdocs/buetow.org/tmp" + directory auto index +} +``` + +and this the `relayd.conf.tpl`: + + +``` +<% + our $primary = $is_primary->($vio0_ip); + our $prefix = $primary ? '' : 'www.'; +%> + +log connection + +tcp protocol "gemini" { + tls keypair <%= $prefix %>foo.zone + tls keypair <%= $prefix %>buetow.org +} + +relay "gemini4" { + listen on <%= $vio0_ip %> port 1965 tls + protocol "gemini" + forward to 127.0.0.1 port 11965 +} + +relay "gemini6" { + listen on <%= $ipv6address->($hostname) %> port 1965 tls + protocol "gemini" + forward to 127.0.0.1 port 11965 +} +``` + +And last but not least, this is the `smtpd.conf.tpl`: + +``` +<% + our $primary = $is_primary->($vio0_ip); + our $prefix = $primary ? '' : 'www.'; +%> + +pki "buetow_org_tls" cert "/etc/ssl/<%= $prefix %>buetow.org.fullchain.pem" +pki "buetow_org_tls" key "/etc/ssl/private/<%= $prefix %>buetow.org.key" + +table aliases file:/etc/mail/aliases +table virtualdomains file:/etc/mail/virtualdomains +table virtualusers file:/etc/mail/virtualusers + +listen on socket +listen on all tls pki "buetow_org_tls" hostname "<%= $prefix %>buetow.org" +#listen on all + +action localmail mbox alias +action receive mbox virtual +action outbound relay + +match from any for domain action receive +match from local for local action localmail +match from local for any action outbound +``` + +## All pieces together + +For the full `Rexfile` example and all the templates, please look at the Git repository: + +=> https://codeberg.org/snonux/rexfiles + +Besides of ACME, also other things, such as DNS server, are rexified too. The following command will run all the Rex tasks and configure everything on my frontend machines automatically: + +``` +rex commons +``` + +The `commons` is a group of task I specified which combines a set of common tasks I always want to execute on all frontend machines. This also includes the ACME tasks mentioned in this article! + +## Conclusion + +ACME and Let's Encrypt greatly help reducing recurring manual maintenance work (creating and renewing certificates). Furthermore, all the certificates are free of costs! I love to use OpenBSD and Rex to automate all of this. + +OpenBSD suits perfectly here as all the tools are already part of the base installation. Rex is not as powerful and popular as other configuration management systems (e.g. Puppet, Chef, SALT or even Ansible). It is more of an underdog and the community is small. But I like underdogs. + +I love the fact that a `Rexfile` is just a Perl DSL. Why re-inventing the wheel? Also, OpenBSD comes with Perl in the base system. So no new programming language had to be added to my mix for the configuration management system. Also, the `acme.sh` shell script is not a Bash but a standard Bourne shell script so that I didn't have to install yet another shell. + +E-Mail me your comments to paul at buetow dot org! + +=> ../ Go back to the main site diff --git a/gemfeed/atom.xml b/gemfeed/atom.xml index ca42dddf..70dea87e 100644 --- a/gemfeed/atom.xml +++ b/gemfeed/atom.xml @@ -1,18 +1,625 @@ - 2022-07-07T22:06:15+03:00 + 2022-07-30T11:35:29+01:00 foo.zone feed To be in the .zone! gemini://foo.zone/ + + Let's Encrypt with OpenBSD and REX + + gemini://foo.zone/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi + 2022-07-30T10:33:58+01:00 + + Paul C. Buetow + comments@mx.buetow.org + + I was amazed how easy it is to automatically generate and update Let's Encrypt certificates with OpenBSD.. .....to read on please visit my site. + +
+

Let's Encrypt with OpenBSD and Rex

+
+                                               /    _    \
+  The Hebern Machine                            \ ." ". /
+                                  ___            /     \
+                              ..""   ""..       |   O   |
+                             /           \      |       |
+                            /             \     |       |
+                          ---------------------------------
+                        _/  o     (O)     o   _            |
+                      _/                    ." ".          |
+                    I/    _________________/     \         |
+                  _/I   ."                        |        |
+          =====  /  I  /                         /         |
+     =====  | | |   \ |       _________________."          |
+=====  | |  | | |   /  \     /  _|_|__|_|_          __     |
+  | |  | |  | | |   \   "._."  /  o    o  \       ."  ".   |
+  |  --|  --|  -|   /          \         _/      /      \  |
+   \____\____\__|   \  ______   |       /       |        | |
+               --------      ---       /        |        | |
+              ( )        (O)          /          \      /  |
+               -----------------------            ".__."   |
+               _|__________________________________________|_
+              /                                              \
+             /________________________________________________\
+                                 ASCII Art by John Savard
+

+

Published by Paul at 2022-07-30

+

I was amazed how easy it is to automatically generate and update Let's Encrypt certificates with OpenBSD.

+

What's Let's Encrypt?

+

Let's Encrypt is a non-profit certificate authority run by Internet Security Research Group that provides X.509 certificates for Transport Layer Security (TLS) encryption at no charge. It is the world's largest certificate authority, used by more than 265 million websites, with the goal of all websites being secure and using HTTPS.

+Source: Wikipedia
+

In short, it gives away TLS certificates for your website - for free! The catch is, that the certificates are only valid for three months. So it is better to automate certificate generation and renevals.

+

Meet acme-client

+

acme-client is the default Automatic Certifcate Management Environment (ACME) client on OpenBSD and part of the OpenBSD base system.

+

When invoked, the client first checks whether certificates actually require to be generated.

+
    +
  • It first checks whether a certificate already exists, if not, it will attempt to generate a new one.
  • +
  • If the certificate already exists but expires within the next 30 days, it will renew it.
  • +
  • Otherwise, `acme-client` won't do anything.
  • +
+

Oversimplified, the following steps are undertaken by acme-client for generating a new certificate:

+
    +
  • Reading its config file /etc/acme-client.conf for a list of hosts (and their alternative names) to generate certificates for. So it means you can also have certificates for abritary subdomains!
  • +
  • Automatic generation of the private certificate part (the certificate key) and the certificate signing request (CSR) to `/etc/ssl/...`.
  • +
  • Requesting Let's Encrypt to sign the certificate. This also includes providing a set of temporary files which will be requested by Let's Encrypt in the next step for verification.
  • +
  • Let's Encrypt then will contact the hostname for the certificate through a special URL (e.g. `http://foo.zone/.well-known/acme-challenge/...`) to verify that the requester is the valid owner of the host.
  • +
  • Let's Encrypt generates a certificate, which then is downloaded to `/etc/ssl/...`.
  • +
+

Configuration

+

There is some (but easy) configuration required to make that all work on OpenBSD.

+

acme-client.conf

+

This is how my /etc/acme-client.conf looks like (I copied a template from /etc/examples/acme-client.conf to /etc/acme-client.conf and added my domains to the bottom:

+
+#
+# $OpenBSD: acme-client.conf,v 1.4 2020/09/17 09:13:06 florian Exp $
+#
+authority letsencrypt {
+    api url "https://acme-v02.api.letsencrypt.org/directory"
+    account key "/etc/acme/letsencrypt-privkey.pem"
+}
+
+authority letsencrypt-staging {
+    api url "https://acme-staging-v02.api.letsencrypt.org/directory"
+    account key "/etc/acme/letsencrypt-staging-privkey.pem"
+}
+
+authority buypass {
+    api url "https://api.buypass.com/acme/directory"
+    account key "/etc/acme/buypass-privkey.pem"
+    contact "mailto:me@example.com"
+}
+
+authority buypass-test {
+    api url "https://api.test4.buypass.no/acme/directory"
+    account key "/etc/acme/buypass-test-privkey.pem"
+    contact "mailto:me@example.com"
+}
+
+domain buetow.org {
+    alternative names { www.buetow.org paul.buetow.org }
+    domain key "/etc/ssl/private/buetow.org.key"
+    domain full chain certificate "/etc/ssl/buetow.org.fullchain.pem"
+    sign with letsencrypt
+}
+
+domain dtail.dev {
+    alternative names { www.dtail.dev }
+    domain key "/etc/ssl/private/dtail.dev.key"
+    domain full chain certificate "/etc/ssl/dtail.dev.fullchain.pem"
+    sign with letsencrypt
+}
+
+domain foo.zone {
+    alternative names { www.foo.zone }
+    domain key "/etc/ssl/private/foo.zone.key"
+    domain full chain certificate "/etc/ssl/foo.zone.fullchain.pem"
+    sign with letsencrypt
+}
+
+domain irregular.ninja {
+    alternative names { www.irregular.ninja }
+    domain key "/etc/ssl/private/irregular.ninja.key"
+    domain full chain certificate "/etc/ssl/irregular.ninja.fullchain.pem"
+    sign with letsencrypt
+}
+
+domain snonux.land {
+    alternative names { www.snonux.land }
+    domain key "/etc/ssl/private/snonux.land.key"
+    domain full chain certificate "/etc/ssl/snonux.land.fullchain.pem"
+    sign with letsencrypt
+}
+

+

httpd.conf

+

For ACME to work you will also need to configure the HTTP daemon so that the "special" ACME requests made from Let's Encrypt are served correctly. I am using the standard OpenBSD httpd here. These are the snippets I use for the foo.zone host in /etc/httpd.conf (of course, you need similar setup for all other hosts as well):

+
+server "foo.zone" {
+  listen on * port 80
+  location "/.well-known/acme-challenge/*" {
+    root "/acme"
+    request strip 2
+  }
+  location * {
+    block return 302 "https://$HTTP_HOST$REQUEST_URI"
+  }
+}
+
+server "foo.zone" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/foo.zone.fullchain.pem"
+    key "/etc/ssl/private/foo.zone.key"
+  }
+  location * {
+    root "/htdocs/gemtexter/foo.zone"
+    directory auto index
+  }
+}
+

+

As you see, plain HTTP only serves the ACME challenge path. Otherwise, it is redirecting the requests to TLS. The TLS section then attempts to use the Let's Encrypt certificates.

+

It is worth noticing that httpd will start without the certificates actually being present. This will cause a certificate error when you try to reach the HTTPS endpoint but it helps to bootstrap Let's Encrypt. As you saw in the config snippet above, Let's Encrypt only requests the plain HTTP endpoint for the verification process so that HTTPS doesn't need to be operational yet at this stage. But once the certificates are generated you will have to reload or restart httpd in order to use any new certificate.

+

CRON job

+

You could now run doas acme-client foo.zone to generate the certificate or to renew it. Or you could automate it with CRON.

+

I have created a script /usr/local/bin/acme.sh for that for all of my domains:

+
+#!/bin/sh
+
+function handle_cert {
+    host=$1
+    # Create symlink, so that relayd also can read it.
+    crt_path=/etc/ssl/$host
+    if [ -e $crt_path.crt ]; then
+        rm $crt_path.crt
+    fi
+    ln -s $crt_path.fullchain.pem $crt_path.crt
+    # Requesting and renewing certificate.
+    /usr/sbin/acme-client -v $host
+}
+
+has_update=no
+handle_cert www.buetow.org
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.paul.buetow.org
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.tmp.buetow.org
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.dtail.dev
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.foo.zone
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.irregular.ninja
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+handle_cert www.snonux.land
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+
+# Pick up the new certs.
+if [ $has_update = yes ]; then
+    /usr/sbin/rcctl reload httpd
+    /usr/sbin/rcctl reload relayd
+    /usr/sbin/rcctl restart smtpd
+fi
+

+

And added the following line to /etc/daily.local to run the script once daily so that certificates will be renewed fully automatically:

+
+/usr/local/bin/acme.sh
+

+

I am receiving a daily output via E-Mail like this now:

+
+Running daily.local:
+acme-client: /etc/ssl/buetow.org.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/paul.buetow.org.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/tmp.buetow.org.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/dtail.dev.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/foo.zone.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/irregular.ninja.fullchain.pem: certificate valid: 80 days left
+acme-client: /etc/ssl/snonux.land.fullchain.pem: certificate valid: 79 days left
+

+

relayd.conf and smtpd.conf

+

Besides of httpd, relayd (mainly for Gemini) and smtpd (for mail, of course) also use TLS certificates. And as you can see in acme.sh, the services are also reloaded or restarted (smtpd doesn't support reload) whenever a certificate was generated or updated.

+

Rexification

+

I didn't write all these configuration files by hand. As a matter of fact, everything is automated with the Rex configuration management system.

+https://www.rexify.org
+

At the top of the Rexfile I define all my hosts:

+
+our @acme_hosts = qw/buetow.org paul.buetow.org tmp.buetow.org dtail.dev foo.zone irregular.ninja snonux.land/;
+

+

General ACME client configuration

+

ACME will be installed into the frontend group of hosts. Here, blowfish is the primary, and twofish the secondary OpenBSD box.

+
+group frontends => 'blowfish.buetow.org', 'twofish.buetow.org';
+

+

This is my Rex task for the general ACME configuration:

+
+desc 'Configure ACME client';
+task 'acme', group => 'frontends',
+  sub {
+    file '/etc/acme-client.conf',
+      content => template('./etc/acme-client.conf.tpl',
+        acme_hosts => \@acme_hosts,
+        is_primary => $is_primary),
+      owner => 'root',
+      group => 'wheel',
+      mode => '644';
+
+    file '/usr/local/bin/acme.sh',
+      content => template('./scripts/acme.sh.tpl',
+        acme_hosts => \@acme_hosts,
+        is_primary => $is_primary),
+      owner => 'root',
+      group => 'wheel',
+      mode => '744';
+
+    file '/etc/daily.local',
+      ensure => 'present',
+      owner => 'root',
+      group => 'wheel',
+      mode => '644';
+
+    append_if_no_such_line '/etc/daily.local', '/usr/local/bin/acme.sh';
+  };
+

+

And there is also a Rex task just to run the ACME script remotely:

+
+desc 'Invoke ACME client';
+task 'acme_invoke', group => 'frontends',
+  sub {
+    say run '/usr/local/bin/acme.sh';
+  };
+
+

+

Furthermore, this snippet (also at the top of the Rexfile) helps to determine whether the current server is the primary server (all hosts will be without the www. prefix) or the secondary server (all hosts will be with the www. prefix):

+
+# Bootstrapping the FQDN based on the server IP as the hostname and domain
+# facts aren't set yet due to the myname file in the first place.
+our $fqdns = sub {
+  my $ipv4 = shift;
+  return 'blowfish.buetow.org' if $ipv4 eq '23.88.35.144';
+  return 'twofish.buetow.org' if $ipv4 eq '108.160.134.135';
+  Rex::Logger::info("Unable to determine hostname for $ipv4", 'error');
+  return 'HOSTNAME-UNKNOWN.buetow.org';
+};
+
+# To determine whether the server is the primary or the secondary.
+our $is_primary = sub {
+  my $ipv4 = shift;
+  $fqdns->($ipv4) eq 'blowfish.buetow.org';
+};
+

+

The following is the acme-client.conf.tpl Rex template file used for the automation. You see here that the www. prefix isn't sent for the primary server. E.g. foo.zone will be served by the primary server (in my case a server located in Germany) and www.foo.zone by the secondary server (in my case a server located in Japan):

+
+#
+# $OpenBSD: acme-client.conf,v 1.4 2020/09/17 09:13:06 florian Exp $
+#
+authority letsencrypt {
+	api url "https://acme-v02.api.letsencrypt.org/directory"
+	account key "/etc/acme/letsencrypt-privkey.pem"
+}
+
+authority letsencrypt-staging {
+	api url "https://acme-staging-v02.api.letsencrypt.org/directory"
+	account key "/etc/acme/letsencrypt-staging-privkey.pem"
+}
+
+authority buypass {
+	api url "https://api.buypass.com/acme/directory"
+	account key "/etc/acme/buypass-privkey.pem"
+	contact "mailto:me@example.com"
+}
+
+authority buypass-test {
+	api url "https://api.test4.buypass.no/acme/directory"
+	account key "/etc/acme/buypass-test-privkey.pem"
+	contact "mailto:me@example.com"
+}
+
+<%
+  our $primary = $is_primary->($vio0_ip);
+  our $prefix = $primary ? '' : 'www.';
+%>
+
+<% for my $host (@$acme_hosts) { %>
+domain <%= $prefix.$host %> {
+	domain key "/etc/ssl/private/<%= $prefix.$host %>.key"
+	domain full chain certificate "/etc/ssl/<%= $prefix.$host %>.fullchain.pem"
+	sign with letsencrypt
+}
+<% } %>
+
+

+

And this is the acme.sh.tpl:

+
+#!/bin/sh
+
+<%
+  our $primary = $is_primary->($vio0_ip);
+  our $prefix = $primary ? '' : 'www.';
+-%>
+
+function handle_cert {
+    host=$1
+    # Create symlink, so that relayd also can read it.
+    crt_path=/etc/ssl/$host
+    if [ -e $crt_path.crt ]; then
+        rm $crt_path.crt
+    fi
+    ln -s $crt_path.fullchain.pem $crt_path.crt
+    # Requesting and renewing certificate.
+    /usr/sbin/acme-client -v $host
+}
+
+has_update=no
+<% for my $host (@$acme_hosts) { -%>
+handle_cert <%= $prefix.$host %>
+if [ $? -eq 0 ]; then
+    has_update=yes
+fi
+<% } -%>
+
+# Pick up the new certs.
+if [ $has_update = yes ]; then
+    /usr/sbin/rcctl reload httpd
+    /usr/sbin/rcctl reload relayd
+    /usr/sbin/rcctl restart smtpd
+fi
+

+

Service rexification

+

These are the Rex tasks setting up httpd, relayd and smtpd services:

+
+desc 'Setup httpd';
+task 'httpd', group => 'frontends',
+  sub {
+    append_if_no_such_line '/etc/rc.conf.local', 'httpd_flags=';
+
+    file '/etc/httpd.conf',
+      content => template('./etc/httpd.conf.tpl',
+        acme_hosts => \@acme_hosts,
+        is_primary => $is_primary),
+      owner => 'root',
+      group => 'wheel',
+      mode => '644',
+      on_change => sub { service 'httpd' => 'restart' };
+
+    service 'httpd', ensure => 'started';
+  };
+
+desc 'Setup relayd';
+task 'relayd', group => 'frontends',
+  sub {
+    append_if_no_such_line '/etc/rc.conf.local', 'relayd_flags=';
+
+    file '/etc/relayd.conf',
+      content => template('./etc/relayd.conf.tpl',
+        ipv6address => $ipv6address,
+        is_primary => $is_primary),
+      owner => 'root',
+      group => 'wheel',
+      mode => '600',
+      on_change => sub { service 'relayd' => 'restart' };
+
+    service 'relayd', ensure => 'started';
+  };
+
+desc 'Setup OpenSMTPD';
+task 'smtpd', group => 'frontends',
+  sub {
+    Rex::Logger::info('Dealing with mail aliases');
+    file '/etc/mail/aliases',
+      source => './etc/mail/aliases',
+      owner => 'root',
+      group => 'wheel',
+      mode => '644',
+      on_change => sub { say run 'newaliases' };
+
+    Rex::Logger::info('Dealing with mail virtual domains');
+    file '/etc/mail/virtualdomains',
+      source => './etc/mail/virtualdomains',
+      owner => 'root',
+      group => 'wheel',
+      mode => '644',
+      on_change => sub { service 'smtpd' => 'restart' };
+
+    Rex::Logger::info('Dealing with mail virtual users');
+    file '/etc/mail/virtualusers',
+      source => './etc/mail/virtualusers',
+      owner => 'root',
+      group => 'wheel',
+      mode => '644',
+      on_change => sub { service 'smtpd' => 'restart' };
+
+    Rex::Logger::info('Dealing with smtpd.conf');
+    file '/etc/mail/smtpd.conf',
+      content => template('./etc/mail/smtpd.conf.tpl',
+        is_primary => $is_primary),
+      owner => 'root',
+      group => 'wheel',
+      mode => '644',
+      on_change => sub { service 'smtpd' => 'restart' };
+
+    service 'smtpd', ensure => 'started';
+  };
+
+

+

This is httpd.conf.tpl:

+
+<%
+  our $primary = $is_primary->($vio0_ip);
+  our $prefix = $primary ? '' : 'www.';
+%>
+
+# Plain HTTP for ACME and HTTPS redirect
+<% for my $host (@$acme_hosts) { %>
+server "<%= $prefix.$host %>" {
+  listen on * port 80
+  location "/.well-known/acme-challenge/*" {
+    root "/acme"
+    request strip 2
+  }
+  location * {
+    block return 302 "https://$HTTP_HOST$REQUEST_URI"
+  }
+}
+<% } %>
+
+# Gemtexter hosts
+<% for my $host (qw/foo.zone snonux.land/) { %>
+server "<%= $prefix.$host %>" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix.$host %>.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix.$host %>.key"
+  }
+  location * {
+    root "/htdocs/gemtexter/<%= $host %>"
+    directory auto index
+  }
+}
+<% } %>
+
+# DTail special host
+server "<%= $prefix %>dtail.dev" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix %>dtail.dev.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix %>dtail.dev.key"
+  }
+  location * {
+    block return 302 "https://github.dtail.dev$REQUEST_URI"
+  }
+}
+
+# Irregular Ninja special host
+server "<%= $prefix %>irregular.ninja" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix %>irregular.ninja.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix %>irregular.ninja.key"
+  }
+  location * {
+    root "/htdocs/irregular.ninja"
+    directory auto index
+  }
+}
+
+# buetow.org special host.
+server "<%= $prefix %>buetow.org" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix %>buetow.org.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix %>buetow.org.key"
+  }
+  block return 302 "https://paul.buetow.org"
+}
+
+server "<%= $prefix %>paul.buetow.org" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix %>paul.buetow.org.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix %>paul.buetow.org.key"
+  }
+  block return 302 "https://foo.zone/contact-information.html"
+}
+
+server "<%= $prefix %>tmp.buetow.org" {
+  listen on * tls port 443
+  tls {
+    certificate "/etc/ssl/<%= $prefix %>tmp.buetow.org.fullchain.pem"
+    key "/etc/ssl/private/<%= $prefix %>tmp.buetow.org.key"
+  }
+  root "/htdocs/buetow.org/tmp"
+  directory auto index
+}
+

+

and this the relayd.conf.tpl:

+
+<%
+  our $primary = $is_primary->($vio0_ip);
+  our $prefix = $primary ? '' : 'www.';
+%>
+
+log connection
+
+tcp protocol "gemini" {
+    tls keypair <%= $prefix %>foo.zone
+    tls keypair <%= $prefix %>buetow.org
+}
+
+relay "gemini4" {
+    listen on <%= $vio0_ip %> port 1965 tls
+    protocol "gemini"
+    forward to 127.0.0.1 port 11965
+}
+
+relay "gemini6" {
+    listen on <%= $ipv6address->($hostname) %> port 1965 tls
+    protocol "gemini"
+    forward to 127.0.0.1 port 11965
+}
+

+

And last but not least, this is the smtpd.conf.tpl:

+
+<%
+  our $primary = $is_primary->($vio0_ip);
+  our $prefix = $primary ? '' : 'www.';
+%>
+
+pki "buetow_org_tls" cert "/etc/ssl/<%= $prefix %>buetow.org.fullchain.pem"
+pki "buetow_org_tls" key "/etc/ssl/private/<%= $prefix %>buetow.org.key"
+
+table aliases file:/etc/mail/aliases
+table virtualdomains file:/etc/mail/virtualdomains
+table virtualusers file:/etc/mail/virtualusers
+
+listen on socket
+listen on all tls pki "buetow_org_tls" hostname "<%= $prefix %>buetow.org"
+#listen on all
+
+action localmail mbox alias <aliases>
+action receive mbox virtual <virtualusers>
+action outbound relay
+
+match from any for domain <virtualdomains> action receive
+match from local for local action localmail
+match from local for any action outbound
+

+

All pieces together

+

For the full Rexfile example and all the templates, please look at the Git repository:

+https://codeberg.org/snonux/rexfiles
+

Besides of ACME, also other things, such as DNS server, are rexified too. The following command will run all the Rex tasks and configure everything on my frontend machines automatically:

+
+rex commons
+

+

The commons is a group of task I specified which combines a set of common tasks I always want to execute on all frontend machines. This also includes the ACME tasks mentioned in this article!

+

Conclusion

+

ACME and Let's Encrypt greatly help reducing recurring manual maintenance work (creating and renewing certificates). Furthermore, all the certificates are free of costs! I love to use OpenBSD and Rex to automate all of this.

+

OpenBSD suits perfectly here as all the tools are already part of the base installation. Rex is not as powerful and popular as other configuration management systems (e.g. Puppet, Chef, SALT or even Ansible). It is more of an underdog and the community is small. But I like underdogs.

+

I love the fact that a Rexfile is just a Perl DSL. Why re-inventing the wheel? Also, OpenBSD comes with Perl in the base system. So no new programming language had to be added to my mix for the configuration management system. Also, the acme.sh shell script is not a Bash but a standard Bourne shell script so that I didn't have to install yet another shell.

+

E-Mail me your comments to paul at buetow dot org!

+
+
+
Sweating the small stuff - Tiny projects of mine gemini://foo.zone/gemfeed/2022-06-15-sweating-the-small-stuff.gmi - 2022-06-15T08:47:44+01:00 + 2022-06-15T10:31:11+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org This blog post is a bit different from the others. It consists of multiple but smaller projects worth mentioning. I got inspired by Julia Evan's 'Tiny programs' blog post and the side projects of The Sephist, so I thought I would also write a blog posts listing a couple of small projects of mine:. .....to read on please visit my site. @@ -251,9 +858,9 @@ v = 008 [v = p*c*(s != c ? 2 : 1)] Total logical CPUs Perl is still a great choice gemini://foo.zone/gemfeed/2022-05-27-perl-is-still-a-great-choice.gmi - 2022-05-27T07:50:12+01:00 + 2022-05-27T10:31:12+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org Perl (the Practical Extraction and Report Language) is a battle-tested, mature, multi-paradigm dynamic programming language. Note that it's not called PERL, neither P.E.R.L. nor Pearl. 'Perl' is the name of the language and 'perl' the name of the interpreter or the interpreter command.. .....to read on please visit my site. @@ -352,9 +959,9 @@ v = 008 [v = p*c*(s != c ? 2 : 1)] Total logical CPUs Creative universe gemini://foo.zone/gemfeed/2022-04-10-creative-universe.gmi - 2022-04-10T10:09:11+01:00 + 2022-04-10T10:31:12+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org I have been participating in an annual work-internal project contest (we call it Pet Project contest) since I moved to London and switched jobs to my current employer. I am very happy to say that I won a 'silver' prize last week here 🎆. Over the last couple of years I have been a finalist in this contest six times and won some kind of prize five times. Some of my projects were also released as open source software. One had a magazine article published, and for another one I wrote an article on my employer's engineering blog. If you have followed all my posts on this blog (the one you are currently reading), then you have probably figured out what these projects were:. .....to read on please visit my site. @@ -459,9 +1066,9 @@ learn () { The release of DTail 4.0.0 gemini://foo.zone/gemfeed/2022-03-06-the-release-of-dtail-4.0.0.gmi - 2022-03-06T18:11:39+00:00 + 2022-03-06T10:31:12+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org I have recently released DTail 4.0.0 and this blog post goes through all the new goodies. You can also read my previous post about DTail in case you wonder what DTail is:. .....to read on please visit my site. @@ -711,9 +1318,9 @@ exec /usr/local/bin/dtailhealth --server localhost:2222 Computer operating systems I use(d) gemini://foo.zone/gemfeed/2022-02-04-computer-operating-systems-i-use.gmi - 2022-02-04T09:58:22+00:00 + 2022-02-04T10:31:13+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org This is a list of Operating Systems I currently use. This list is in no particular order and also will be updated over time. The very first operating system I used was MS-DOS (mainly for games) and the very first Unix like operating system I used was SuSE Linux 5.3. My first smartphone OS was Symbian on a clunky Sony Ericsson device.. .....to read on please visit my site. @@ -877,9 +1484,9 @@ GNU/kFreeBSD rhea.buetow.org 8.0-RELEASE-p5 FreeBSD 8.0-RELEASE-p5 #2: Sat Nov 2 Welcome to the foo.zone gemini://foo.zone/gemfeed/2022-01-23-welcome-to-the-foo.zone.gmi - 2022-01-23T16:42:04+00:00 + 2022-01-23T10:31:14+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org I don't count this as a real blog post, but more of an announcement (I aim to write one real post once monthly). From now on, 'foo.zone' is the new address of this site. All other addresses will still forward to it and eventually (based on the traffic still going through) will be deactivated.. .....to read on please visit my site. @@ -924,9 +1531,9 @@ GNU/kFreeBSD rhea.buetow.org 8.0-RELEASE-p5 FreeBSD 8.0-RELEASE-p5 #2: Sat Nov 2 Bash Golf Part 2 gemini://foo.zone/gemfeed/2022-01-01-bash-golf-part-2.gmi - 2022-01-01T23:36:15+00:00 + 2022-01-01T10:31:14+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org This is the second blog post about my Bash Golf series. This series is random Bash tips, tricks and weirdnesses I came across. It's a collection of smaller articles I wrote in an older (in German language) blog, which I translated and refreshed with some new content.. .....to read on please visit my site. @@ -1336,12 +1943,12 @@ PAUL:X:1000:1000:PAUL BUETOW:/HOME/PAUL:/BIN/BASH How to stay sane as a DevOps person gemini://foo.zone/gemfeed/2021-12-26-how-to-stay-sane-as-a-devops-person.gmi - 2021-12-26T12:02:02+00:00 + 2021-12-26T10:31:15+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Log4shell (CVE-2021-44228) made it clear, once again, that working in information technology is not an easy job (especially when you are a DevOps/SRE or a security engineer). I thought it would be interesting to summarize a few techniques to help you to relax.. .....to read on please visit my site. + Log4shell (CVE-2021-44228) made it clear, once again, that working in information technology is not an easy job (especially when you are a DevOps person). I thought it would be interesting to summarize a few techniques to help you to relax.. .....to read on please visit my site.

How to stay sane as a DevOps person

@@ -1428,12 +2035,12 @@ PAUL:X:1000:1000:PAUL BUETOW:/HOME/PAUL:/BIN/BASH Bash Golf Part 1 gemini://foo.zone/gemfeed/2021-11-29-bash-golf-part-1.gmi - 2021-11-29T14:06:14+00:00 + 2021-11-29T10:31:15+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - This is the first blog post about my Bash Golf series. This series is random Bash tips, tricks and weirdnesses I came across. It's a collection of smaller articles I wrote in an older (in German language) blog, which I translated and refreshed with some new content.. .....to read on please visit my site. + This is the first blog post about my Bash Golf series. This series is about random Bash tips, tricks and weirdnesses I came across. It's a collection of smaller articles I wrote in an older (in German language) blog, which I translated and refreshed with some new content.. .....to read on please visit my site.

Bash Golf Part 1

@@ -1811,9 +2418,9 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is Defensive DevOps gemini://foo.zone/gemfeed/2021-10-22-defensive-devops.gmi - 2021-10-22T10:02:46+03:00 + 2021-10-22T10:31:16+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org I have seen many different setups and infrastructures during my carreer. My roles always included front-line ad-hoc fire fighting production issues. This often involves identifying and fixing these under time pressure, without the comfort of 2-week-long SCRUM sprints and without an exhaustive QA process. I also wrote a lot of code (Bash, Ruby, Perl, Go, and a little Java), and I followed the typical software development process, but that did not always apply to critical production issues.. .....to read on please visit my site. @@ -1890,9 +2497,9 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is Keep it simple and stupid gemini://foo.zone/gemfeed/2021-09-12-keep-it-simple-and-stupid.gmi - 2021-09-12T09:39:20+03:00 + 2021-09-12T10:31:16+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org A robust computer system must be kept simple and stupid (KISS). The fancier the system is, the more can break. Unfortunately, most systems tend to become complex and challenging to maintain in today's world. In the early days, so I was told, engineers understood every part of the system, but nowadays, we see more of the 'lasagna' stack. One layer or framework is built on top of another layer, and in the end, nobody has got a clue what's going on.. .....to read on please visit my site. @@ -1960,9 +2567,9 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is On being Pedantic about Open-Source gemini://foo.zone/gemfeed/2021-08-01-on-being-pedantic-about-open-source.gmi - 2021-08-01T10:37:58+03:00 + 2021-08-01T10:31:17+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org I believe that it is essential to always have free and open-source alternatives to any kind of closed-source proprietary software available to choose from. But there are a couple of points you need to take into consideration. . .....to read on please visit my site. @@ -2039,12 +2646,12 @@ bash: line 1: 1/10.0 : syntax error: invalid arithmetic operator (error token is The Well-Grounded Rubyist gemini://foo.zone/gemfeed/2021-07-04-the-well-grounded-rubyist.gmi - 2021-07-04T10:51:23+01:00 + 2021-07-04T10:31:17+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - When I was a Linux System Administrator, I have been programming in Perl for years. I still maintain some personal Perl programming projects (e.g. Xerl, guprecords, Loadbars). After switching jobs a couple of years ago (becoming a Site Reliability Engineer), I found Ruby (and some Python) widely used there. As I wanted to do something new, I then decided to give Ruby a go for all medium-sized programming and scripting projects.. .....to read on please visit my site. + When I was a Linux System Administrator, I have been programming in Perl for years. I still maintain some personal Perl programming projects (e.g. Xerl, guprecords, Loadbars). After switching jobs a couple of years ago (becoming a Site Reliability Engineer), I found Ruby (and some Python) widely used there. As I wanted to do something new, I decided to give Ruby a go.. .....to read on please visit my site.

The Well-Grounded Rubyist

@@ -2120,9 +2727,9 @@ Hello World Gemtexter - One Bash script to rule it all gemini://foo.zone/gemfeed/2021-06-05-gemtexter-one-bash-script-to-rule-it-all.gmi - 2021-06-05T19:03:32+01:00 + 2021-06-05T10:31:17+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org You might have read my previous blog post about entering the Geminispace, where I pointed out the benefits of having and maintaining an internet presence there. This whole site (the blog and all other pages) is composed in the Gemtext markup language. . .....to read on please visit my site. @@ -2259,12 +2866,12 @@ assert::equals "$(generate::make_link md "$gemtext")" \ Personal Bash coding style guide gemini://foo.zone/gemfeed/2021-05-16-personal-bash-coding-style-guide.gmi - 2021-05-16T14:51:57+01:00 + 2021-05-16T10:31:18+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Lately, I have been polishing and writing a lot of Bash code. Not that I never wrote a lot of Bash, but now as I also looked through the 'Google Shell Style Guide' I thought it is time to also write my own thoughts on that. I agree to that guide in most, but not in all points. . .....to read on please visit my site. + Lately, I have been polishing and writing a lot of Bash code. Not that I never wrote a lot of Bash, but now as I also looked through the Google Shell Style Guide, I thought it is time also to write my thoughts on that. I agree with that guide in most, but not in all points. . .....to read on please visit my site.

Personal Bash coding style guide

@@ -2567,12 +3174,12 @@ fi Welcome to the Geminispace gemini://foo.zone/gemfeed/2021-04-24-welcome-to-the-geminispace.gmi - 2021-04-24T19:28:41+01:00 + 2021-04-24T10:31:19+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Have you reached this article already via Gemini? You need a special client for that, web browsers such as Firefox, Chrome, Safari etc. don't support the Gemini protocol. The Gemini address of this site (or the address of this capsule as people say in Geminispace) is: ... to read on visit my site. + Have you reached this article already via Gemini? It requires a Gemini client; web browsers such as Firefox, Chrome, Safari, etc., don't support the Gemini protocol. The Gemini address of this site (or the address of this capsule as people say in Geminispace) is:. .....to read on please visit my site.

Welcome to the Geminispace

@@ -2637,12 +3244,12 @@ fi DTail - The distributed log tail program gemini://foo.zone/gemfeed/2021-04-22-dtail-the-distributed-log-tail-program.gmi - 2021-04-22T19:28:41+01:00 + 2021-04-22T10:31:19+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - This article first appeared at the Mimecast Engineering Blog but I made it available here in my personal Gemini capsule too. ...to read on visit my site. + This article first appeared at the Mimecast Engineering Blog but I made it available here in my personal internet site too.. .....to read on please visit my site.

DTail - The distributed log tail program

@@ -2718,9 +3325,9 @@ dtail –servers serverlist.txt –files ‘/var/log/*.log’ –regex ‘(?i:er Realistic load testing with I/O Riot for Linux gemini://foo.zone/gemfeed/2018-06-01-realistic-load-testing-with-ioriot-for-linux.gmi - 2018-06-01T14:50:29+01:00 + 2018-06-01T10:31:19+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org This text first was published in the german IT-Administrator computer Magazine. 3 years have passed since and I decided to publish it on my blog too. . .....to read on please visit my site. @@ -2857,9 +3464,9 @@ Total time: 1213.00s Object oriented programming with ANSI C gemini://foo.zone/gemfeed/2016-11-20-object-oriented-programming-with-ansi-c.gmi - 2016-11-20T22:10:57+00:00 + 2016-11-20T10:31:20+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org You can do a little of object-oriented programming in the C Programming Language. However, that is, in my humble opinion, limited. It's easier to use a different programming language than C for OOP. But still it's an interesting exercise to try using C for this.. .....to read on please visit my site. @@ -2949,12 +3556,12 @@ mult.calculate(mult,a,b)); Spinning up my own authoritative DNS servers gemini://foo.zone/gemfeed/2016-05-22-spinning-up-my-own-authoritative-dns-servers.gmi - 2016-05-22T18:59:01+01:00 + 2016-05-22T10:31:20+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Finally, I had time to deploy my own authoritative DNS servers (master and slave) for my domains 'buetow.org' and 'buetow.zone'. My domain name provider is Schlund Technologies. They allow their customers to manually edit the DNS records (BIND files). And they also give you the opportunity to set your own authoritative DNS servers for your domains. From now I am making use of that option.. .....to read on please visit my site. + Finally, I had time to deploy my authoritative DNS servers (master and slave) for my domains 'buetow.org' and 'buetow.zone'. My domain name provider is Schlund Technologies. They allow their customers to edit the DNS records (BIND files) manually. And they also allow you to set your authoritative DNS servers for your domains. From now, I am making use of that option.. .....to read on please visit my site.

Spinning up my own authoritative DNS servers

@@ -3174,12 +3781,12 @@ apply Service "dig6" { Offsite backup with ZFS (Part 2) gemini://foo.zone/gemfeed/2016-04-16-offsite-backup-with-zfs-part2.gmi - 2016-04-16T22:43:42+01:00 + 2016-04-16T10:31:20+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - I enhanced the procedure a bit. From now on I am having two external 2TB USB hard drives. Both are setup exactly the same way. To decrease the probability that they will not fail at about the same time both drives are of different brands. One drive is kept at the secret location. The other one is kept at home right next to my HP MicroServer. ...to read on visit my site. + I enhanced the procedure a bit. From now on, I have two external 2TB USB hard drives. Both are set up precisely the same way. To decrease the probability that both drives will not fail simultaneously, they are of different brands. One drive is kept at a secret location. The other one is held at home, right next to my HP MicroServer.. .....to read on please visit my site.

Offsite backup with ZFS (Part 2)

@@ -3211,9 +3818,9 @@ apply Service "dig6" { Jails and ZFS with Puppet on FreeBSD gemini://foo.zone/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.gmi - 2016-04-09T18:29:47+01:00 + 2016-04-09T10:31:20+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org Over the last couple of years I wrote quite a few Puppet modules in order to manage my personal server infrastructure. One of them manages FreeBSD Jails and another one ZFS file systems. I thought I would give a brief overview in how it looks and feels.. .....to read on please visit my site. @@ -3590,12 +4197,12 @@ Notice: Finished catalog run in 206.09 seconds Offsite backup with ZFS gemini://foo.zone/gemfeed/2016-04-03-offsite-backup-with-zfs.gmi - 2016-04-03T22:43:42+01:00 + 2016-04-03T10:31:21+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - When it comes to data storage and potential data loss I am a paranoid person. It is not just due to my job but also due to a personal experience I encountered over 10 years ago: A single drive failure and loss of all my data (pictures, music, ....). ...to read on visit my site. + When it comes to data storage and potential data loss, I am a paranoid person. It is due to my job and a personal experience I encountered over ten years ago: A single drive failure and loss of all my data (pictures, music, etc.).. .....to read on please visit my site.

Offsite backup with ZFS

@@ -3633,12 +4240,12 @@ Notice: Finished catalog run in 206.09 seconds Run Debian on your phone with Debroid gemini://foo.zone/gemfeed/2015-12-05-run-debian-on-your-phone-with-debroid.gmi - 2015-12-05T16:12:57+00:00 + 2015-12-05T10:31:21+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - You can use the following tutorial to install a full blown Debian GNU/Linux Chroot on a LG G3 D855 CyanogenMod 13 (Android 6). First of all you need to have root permissions on your phone and you also need to have the developer mode activated. The following steps have been tested on Linux (Fedora 23). .....to read on please visit my site. + You can use the following tutorial to install a full-blown Debian GNU/Linux Chroot on an LG G3 D855 CyanogenMod 13 (Android 6). First of all, you need to have root permissions on your phone, and you also need to have the developer mode activated. The following steps have been tested on Linux (Fedora 23).. .....to read on please visit my site.

Run Debian on your phone with Debroid

@@ -3794,15 +4401,15 @@ exit - The fibonacci.pl.c Polyglot + The fibonacci.pl.raku.c Polyglot gemini://foo.zone/gemfeed/2014-03-24-the-fibonacci.pl.c-polyglot.gmi - 2014-03-24T21:32:53+00:00 + 2014-03-24T10:31:22+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - In computing, a polyglot is a computer program or script written in a valid form of multiple programming languages, which performs the same operations or output independent of the programming language used to compile or interpret it. .....to read on please visit my site. + In computing, a polyglot is a computer program or script written in a valid form of multiple programming languages, which performs the same operations or output independent of the programming language used to compile or interpret it.. .....to read on please visit my site.

The fibonacci.pl.raku.c Polyglot

@@ -3939,12 +4546,12 @@ fib(10) = 55 Perl Daemon (Service Framework) gemini://foo.zone/gemfeed/2011-05-07-perl-daemon-service-framework.gmi - 2011-05-07T22:26:02+01:00 + 2011-05-07T10:31:22+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - PerlDaemon is a minimal daemon for Linux and other Unix like operating systems programmed in Perl. It is a minimal but pretty functional and fairly generic service framework. This means that it does not do anything useful other than providing a framework for starting, stopping, configuring and logging. In order to do something a module (written in Perl) bust be provided.. .....to read on please visit my site. + PerlDaemon is a minimal daemon for Linux and other Unix like operating systems programmed in Perl. It is a minimal but pretty functional and fairly generic service framework. This means that it does not do anything useful other than providing a framework for starting, stopping, configuring and logging. To do something useful, a module (written in Perl) must be provided.. .....to read on please visit my site.

Perl Daemon (Service Framework)

@@ -4085,12 +4692,12 @@ sub do ($) { The Fype Programming Language gemini://foo.zone/gemfeed/2010-05-09-the-fype-programming-language.gmi - 2010-05-09T12:48:29+01:00 + 2010-05-09T10:31:22+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Fype is an interpreted programming language created by me for learning and fun. The interpreter is written in C. It has been tested on FreeBSD and NetBSD and may also work on other Unix like operating systems such as Linux based ones. To be honest, besides learning and fun there is really no other use case of why Fype actually exists as many other programming languages are much faster and more powerful.. .....to read on please visit my site. + Fype is an interpreted programming language created by me for learning and fun. The interpreter is written in C. It has been tested on FreeBSD and NetBSD and may also work on other Unix like operating systems such as Linux based ones. Besides learning and fun, there is no other use case of why Fype exists as many other programming languages are much faster and more powerful.. .....to read on please visit my site.

The Fype Programming Language

@@ -4500,12 +5107,12 @@ BB Lazy Evaluation with Standard ML gemini://foo.zone/gemfeed/2010-05-07-lazy-evaluation-with-standarn-ml.gmi - 2010-05-07T08:17:59+01:00 + 2010-05-07T10:31:23+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - In contrast to Haskell, Standard SML does not use lazy evaluation by default, but strict evaluation. . .....to read on please visit my site. + In contrast to Haskell, Standard SML does not use lazy evaluation by default but an eager evaluation. . .....to read on please visit my site.

Lazy Evaluation with Standard ML

@@ -4600,12 +5207,12 @@ first 10 nat_pairs_not_null Standard ML and Haskell gemini://foo.zone/gemfeed/2010-04-09-standard-ml-and-haskell.gmi - 2010-04-09T22:57:36+01:00 + 2010-04-09T10:31:24+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - I am currently looking into the functional programming language Standard ML (aka SML). The purpose is to refresh my functional programming skills and to learn something new too. Since I already know a little Haskell, could I do not help myself and I implemented the same exercises in Haskell too.. .....to read on please visit my site. + I am currently looking into the functional programming language Standard ML (aka SML). The purpose is to refresh my functional programming skills and to learn something new too. Since I already knew a little Haskell, I could not help myself, and I also implemented the same exercises in Haskell.. .....to read on please visit my site.

Standard ML and Haskell

@@ -4754,12 +5361,12 @@ my_filter f l = foldr (make_filter_fn f) [] l Using my Nokia N95 for fixing my MTA gemini://foo.zone/gemfeed/2008-12-29-using-my-nokia-n95-for-fixing-my-mta.gmi - 2008-12-29T09:10:41+00:00 + 2008-12-29T10:31:24+00:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - The last week I was in Vidin, Bulgaria with no internet access and I had to fix my MTA (Postfix) at. .....to read on please visit my site. + The last week I was in Vidin, Bulgaria with no internet access and I had to fix my MTA (Postfix) at host.0.buetow.org which serves E-Mail for all my customers at P. B. Labs. Good, that I do not guarantee high availability on my web services (I've to do a full time job somewhere else too). . .....to read on please visit my site.

Using my Nokia N95 for fixing my MTA

@@ -4800,12 +5407,12 @@ _jgs_\|//_\\|///_\V/_\|//__ Perl Poetry gemini://foo.zone/gemfeed/2008-06-26-perl-poetry.gmi - 2008-06-26T21:43:51+01:00 + 2008-06-26T10:31:24+01:00 - Paul Buetow + Paul C. Buetow comments@mx.buetow.org - Here are some Perl Poems I wrote. They don't do anything useful when you run them but they don't produce a compiler error either. They only exists for fun and demonstrate what you can do with Perl syntax.. .....to read on please visit my site. + Here are some Perl Poems I wrote. They don't do anything useful when you run them, but they don't produce a compiler error either. They only exist for fun and demonstrate what you can do with Perl syntax.. .....to read on please visit my site.

Perl Poetry

diff --git a/gemfeed/index.gmi b/gemfeed/index.gmi index d73b35ad..2a7aa6e2 100644 --- a/gemfeed/index.gmi +++ b/gemfeed/index.gmi @@ -2,6 +2,7 @@ ## To be in the .zone! +=> ./2022-07-30-lets-encrypt-with-openbsd-and-rex.gmi 2022-07-30 - Let's Encrypt with OpenBSD and Rex => ./2022-06-15-sweating-the-small-stuff.gmi 2022-06-15 - Sweating the small stuff - Tiny projects of mine => ./2022-05-27-perl-is-still-a-great-choice.gmi 2022-05-27 - Perl is still a great choice => ./2022-04-10-creative-universe.gmi 2022-04-10 - Creative universe -- cgit v1.2.3