Secure Webserver (httpd+SSL)

Introduction

In order to secure the data transmitted between the webserver and the client, we need SSL. There are multiple types of SSL certificates and they mostly differ in the way the identity of the requester is verified. For example, banks will usually choose the strictest verification method as it will show prominently in the browsers searchbar or omnibox. Since these verification methods take time and effort, they are more costly the more work has to be put in.

The most basic form of authentication is the so-called domain-level verification: if you control the domain, you’re allowed to receive an SSL certificate. Let’s Encrypt is a project that allows domain owners to receive such a domain-level verified certificate for free and we’ll use their service in this tutorial.

You are expected to have root privileges for all commands below.

ACME Configuration

First we need to edit the /etc/acme-client.conf file that is already present on the system:

  • Comment in the section that was commented out
  • Change example.com to the domain name you want to secure
  • Add any alternative names like www.example.com

    #
    # $OpenBSD: acme-client.conf,v 1.6 2017/11/27 01:59:55 florian Exp $
    #
    authority letsencrypt {
        api url "https://acme-v01.api.letsencrypt.org/directory"
        account key "/etc/acme/letsencrypt-privkey.pem"
    }
    
    authority letsencrypt-staging {
        api url "https://acme-staging.api.letsencrypt.org/directory"
        account key "/etc/acme/letsencrypt-staging-privkey.pem"
    }
    
    domain example.com {
       alternative names { www.example.com }
       domain key "/etc/ssl/private/example.com.key"
       domain certificate "/etc/ssl/example.com.crt"
       domain full chain certificate "/etc/ssl/example.com.fullchain.pem"
       sign with letsencrypt
    }
    

Create the necessary directories

mkdir -p -m 700 /etc/acme
mkdir -p -m 700 /etc/ssl/acme/private
mkdir -p -m 755 /var/www/acme

Let’s Encrypt will make a verification request so we need to update /etc/httpd.conf to be able to handle these requests:

In OpenBSD 6.4 the syntax for stripping changed.

For OpenBSD version 6.4 and later

server "www.example.com" {
  listen on * port 80
  root "/htdocs/www.example.com"
  location "/.well-known/acme-challenge/*" {
    root "/acme"
    request strip 2
  }
}

server "example.com" {
  listen on * port 80
  block return 301 "http://www.example.com$REQUEST_URI"
}

For previous OpenBSD versions

server "www.example.com" {
  listen on * port 80
  root "/htdocs/www.example.com"
  location "/.well-known/acme-challenge/*" {
    root { "/acme", strip 2 }
  }
}

server "example.com" {
  listen on * port 80
  block return 301 "http://www.example.com$REQUEST_URI"
}

Next, we need to check the configuration and reload or restart the daemon

httpd -n && rcctl restart httpd

We now can run the acme-client to create a new account and a key

acme-client -vAD www.example.com

The final lines of the output should be something like:

acme-client: /etc/ssl/example.com.crt: created
acme-client: /etc/ssl/example.com.pem: created

The certificates are valid for 90 days to we need to setup a cronjob to renew. We will run it daily but only once the end of the validity comes in sight, will the certificates actually be renewed.

crontab -e

insert a line like:

05 3 * * * acme-client example.com && rcctl reload httpd

httpd configuration

We now have everything we need to correctly configure httpd to run SSL. Change the httpd.conf file to look like this.

In OpenBSD 6.4 the syntax for stripping changed.

For OpenBSD version 6.4 and later

server "www.example.com" {
  listen on * tls port 443
  root "/htdocs/www.example.com"
  tls {
    certificate "/etc/ssl/example.com.fullchain.pem"
    key "/etc/ssl/private/example.com.key"
  }
  location "/.well-known/acme-challenge/*" {
    root "/acme"
    request strip 2
  }
}

server "example.com" {
  listen on * tls port 443
  tls {
    certificate "/etc/ssl/example.com.fullchain.pem"
    key "/etc/ssl/private/example.com.key"
  }
  block return 301 "https://example.com$REQUEST_URI"
}

server "www.example.com" {
  listen on * port 80
  alias "example.com"
  block return 301 "https://www.example.com$REQUEST_URI"
}

For previous OpenBSD versions

server "www.example.com" {
  listen on * tls port 443
  root "/htdocs/www.example.com"
  tls {
    certificate "/etc/ssl/example.com.fullchain.pem"
    key "/etc/ssl/private/example.com.key"
  }
  location "/.well-known/acme-challenge/*" {
    root { "/acme", strip 2 }
  }
}

server "example.com" {
  listen on * tls port 443
  tls {
    certificate "/etc/ssl/example.com.fullchain.pem"
    key "/etc/ssl/private/example.com.key"
  }
  block return 301 "https://example.com$REQUEST_URI"
}

server "www.example.com" {
  listen on * port 80
  alias "example.com"
  block return 301 "https://www.example.com$REQUEST_URI"
}

Restart httpd:

httpd -n && rcctl restart httpd

SSL verification

Additional domains

First, we need to move the existing certificate out of the way. Skipping this step will result in an error.

mv /etc/ssl/example.com.crt /etc/ssl/example.com.crt.bak

Edit /etc/acme-client.conf and add more alternative names

alternative names { www.example.com example1.com www.example1.com }

Update httpd.conf accordingly:

server "www.example1.com" {
  listen on * tls port 443
  root "/htdocs/www.example1.com"
  tls {
    certificate "/etc/ssl/example.com.pem"
    key "/etc/ssl/private/example.com.key"
  }
  location "/.well-known/acme-challenge/*" {
    root { "/acme", strip 2 }
  }
}

Run the acme client again

acme-client -vFAD example.com

And restart httpd

httpd -n && rcctl restart httpd