Extended Brain Storage

OpenBSD: HTTPD with TLS Support Using ACME

Posted on September 19, 2016

A quick introduction of how to automate Let's Encrypt's TLS certificates process using httpd and acme-client in OpenBSD...

General Theory

In order to be generally accepted by the majority of operating systems (OSs), the Transport Layer Security (TLS) X.509 certificates (as per RFC5280) need to be signed by a certification authority (CA) recognised by the particular OS. Because the related process of creation, validation, signing, installation and renewal of certificates is resource hungry, it has been commercialised. However, the Let's Encrypt CA (founded by Electronic Frontier Foundation, Mozilla Foundation, University of Michigan, Akamai Technologies and Cisco Systems) provides free X.509 certificates for TLS encryption via an automated process.

Automated Certificate Management Environment (ACME) is a challenge-response protocol that was designed in order to verify a particular domain name ownership before the certificate is issued. Using DNS entries, the process involves several requests to a web server on all domains that are covered by the Subject (Common Name) and Certificate Subject Alt Name (within certificate's Extensions field). For that purpose, some sort of an ACME client software is needed.

Important Note: There are discussions (e.g. here and here) whether DNS records must be A records and not CNAME aliases. Generally, CNAMEs are fine. Reader discretion advised!

OpenBSD comes with necessary software already installed, i.e. httpd and acme-client.

HTTPD

The HTTP daemon called httpd is a HTTP server with TLS and FastCGI support (i.e. a binary protocol for interfacing interactive programs such as PHP). For the purpose of ACME, PHP is not needed.

Due to the fact that in OpenBSD, the httpd is chrooted in /var/www, it is necessary to copy some files into it as follows:

$ mkdir -p /var/www/etc/
$ cp /etc/{hosts,localtime,resolv.conf,services} /var/www/etc/

Considering that:

  1. the domain names domain.tld and www.domain.tld are:
  1. the TLS support is required for both domain names;

the httpd can be configured in httpd.conf as follows:

$ vi /etc/httpd.conf
### The default host (EGRESS_IP:80)
server "default" {
  listen on * port 80
  block return 301 "http://www.domain.tld$REQUEST_URI"
}
### domain.tld:80
server "domain.tld" {
  listen on * port 80
  block return 301 "http://www.domain.tld$REQUEST_URI"
}
### www.domain.tld:80
server "www.domain.tld" {
  listen on * port 80
  log style combined
  # ACME
  location "/.well-known/acme-challenge/*" {
    root "/acme"
    root strip 2
  }
  # ROOT
  location "/*" {
    block return 301 "https://www.domain.tld$REQUEST_URI"
  }
}

The httpd.conf makes sure that HTTP requests (to TCP port 80) made to the web server's:

  1. public (EGRESS_IP) address are redirected (with HTTP/1.1 301 Moved Permanently) to http://www.domain.tld (including the URI),
  2. domain.tld domain name are redirected as in the previous step,
  3. www.domain.tld domain name are handled as follows:

Start (or restart) of the HTTPD is straightforward:

$ rcctl restart httpd

DNS Certification Authority Authorization

The Certification Authority Authorization (CAA) was specified in RFC6844 in 2013. It operates via a new DNS resource record (RR) called CAA (type 257). Before issuing a certificate, CAs are expected to check the DNS record and refuse issuance unless they find themselves on the whitelist. Considering the Let's Encrypt CA, the DNS RR looks as follows:

domain.tld. CAA 128 issue "letsencrypt.org"

In addition to the issue directive, two other directives are supported:

Furthermore, the number 128 is a flags byte with its highest bit set, meaning the directive use is considered critical and must be followed. At a high level, the CAA has a similar purpose to public key pinning (HPKP), but the implementation is entirely different.

After the DNS update is completed, the verification can be accomplished as follows:

$ dig caa domain.tld +short

More info can be found on qualys.com.


New Certificate Using ACME Client

Considering that all DNS records are in place, such as: domain.tld and www.domain.tld in IPv4 environment:

$ dig +noall +answer domain.tld
domain.tld.		TTL	IN	A	PUBLIC_IP_ADDRESS
$ dig +noall +answer www.domain.tld
www.domain.tld.		TTL	IN	A	PUBLIC_IP_ADDRESS

the Automatic Certificate Management Environment (ACME) client software can be configured in the acme-client.conf file. Separating multiple alternative names by spaces, the domain section can be configured as follows:

$ vi /etc/acme-client.conf
#
# $OpenBSD: acme-client.conf,v 1.2 2019/06/07 08:08:30 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"
}

domain domain.tld {
        alternative names { www.domain.tld }
        domain key "/etc/ssl/private/domain.tld.key"
        domain full chain certificate "/etc/ssl/domain.tld.fullchain.pem"
        sign with letsencrypt
}

In order to initialize a new Account and Domain key, the following needs to be run:

# acme-client -ADv domain.tld
acme-client: /etc/acme/letsencrypt-privkey.pem: generated RSA account key
acme-client: /etc/ssl/private/domain.rld.key: generated RSA domain key
acme-client: https://acme-v02.api.letsencrypt.org/directory: directories
acme-client: acme-v02.api.letsencrypt.org: DNS: 172.65.32.248
acme-client: https://acme-v02.api.letsencrypt.org/acme/new-reg: new-reg
acme-client: https://acme-v02.api.letsencrypt.org/acme/new-authz: req-auth: domain.tld
acme-client: https://acme-v02.api.letsencrypt.org/acme/new-authz: req-auth: www.domain.tld
...
acme-client: https://acme-v02.api.letsencrypt.org/acme/new-cert: certificate
acme-client: http://cert.int-x3.letsencrypt.org/: full chain
acme-client: cert.int-x3.letsencrypt.org: DNS: 104.101.237.20
acme-client: /etc/ssl/domain.tld.crt: created
acme-client: /etc/ssl/domain.tld.fullchain.pem: created

For an automatic certificate refresh, a daily cron task can be created as follows:

$ echo "#\!/bin/sh\nacme-client domain.tld && rcctl reload httpd" >> /etc/daily.local

HTTPD Update to Support HTTPS

Considering that the ACME process has been successful, the certificates are located in:

Since the httpd.conf file can include references to other configuration files, it may seem worth to separate the configuration of HTTPS virtual hosts by adding the following lines:

$ vi /etc/httpd.conf
### HTTPS SETUP
include "/etc/httpd.conf.https"

The HTTPS-enabled virtual hosts can be defined as follows:

$ vi /etc/httpd.conf.https
### domain.tld:443
server "domain.tld" {
  listen on * tls port 443
  log style combined
  tls {
    certificate "/etc/ssl/domain.tld.fullchain.pem"
    key "/etc/ssl/private/domain.tld.key"
    ciphers "TLSv1.2:TLSv1.3:!CAMELLIA:!ARIA:!DSS:!ADH:!PSK:!RSA:!ECDHE-RSA-AES128-SHA256:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES128-SHA256"
  }
  hsts { subdomains }
  block return 301 "https://www.domain.tld$REQUEST_URI"
}
### www.domain.tld:443
server "www.domain.tld" {
  listen on * tls port 443
  log style combined
  tls {
    certificate "/etc/ssl/domain.tld.fullchain.pem"
    key "/etc/ssl/private/domain.tld.key"
    ciphers "TLSv1.2:TLSv1.3:!CAMELLIA:!ARIA:!DSS:!ADH:!PSK:!RSA:!ECDHE-RSA-AES128-SHA256:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES128-SHA256"
  }
  hsts { subdomains }
  directory {
    index "index.html" # HTTPd supports a single file only; either .html or .php
  }
  # Last, allow access to the root directory
  location "/*" {
    root { "/htdocs/www.domain.tld" }
  }
}

The HTTPS configuration uses the same logic as in the plain HTTP section. The following keywords require a commentary, though:

HTTPD restart is necessary in order to apply the changes:

$ rcctl restart httpd

Verification

Verification can be done in many ways, one of which is to create a simple index.html file in the specific document root (/htdocs/www.domain.tld):

$ mkdir /var/www/htdocs/www.domain.tld
$ echo "It works!" > /var/www/htdocs/www.domain.tld/index.html

Opening the https://www.domain.tld in a favourite web browser should work flawlessly as well as the configured redirections.


Certificate Revocation Using ACME Client

Sometimes, it may be necessary to revoke the current certificate due to:

Revocation can be performed as follows:

$ acme-client -rv domain.tld

The alternative names section in the ACME's configuration file can be updated (/etc/acme-client.conf), and the certificate request re-submitted as follows:

$ acme-client -v domain.tld

HTTPD can be updated and restarted in order to apply the changes:

$ rcctl restart httpd

The robots.txt File

The robots.txt file can be created to filter out allowed User-agents. Let's start with a "allow all" setup, which can be filtered later if necessary:

$ echo "User-agent: *\nDisallow:" > /var/www/htdocs/www.domain.tld/robots.txt

Tags: #OpenBSD #security #httpd #HTTPS #TLS #X.509 #certificate #authority #ACME

⏴ Previous Post Next Post ⏵