Eric Radman : a Journal

Run Your Own Server

Configuring your own box on the Internet is a lot of fun, and provides an opportunity to experiment with Internet services in general.

Service Cost Provider
Domain registration $11/yr
Secondary DNS $36/yr
OpenBSD VPS hosting $15-$20/mo

VPS Hosting

Initial Setup

Getting started with a service like is easy, after you select a plan and enter your data an e-mail will be sent with your initial login information. First login and change your login credentials.

ssh sshadmin@
useradd -m eradman
vi /etc/group
userdel sshadmin
rm -rf /home/sshadmin

Out of the gate you may also want to prevent password authentication for anyone not in the weel group.

# Require keys for regular users
Match Group !wheel
    PasswordAuthentication no

Then set the hostname and timezone

echo "" > /etc/myname
ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime

If you ever need to repair your install your hosting provider should be able to give you access via VNC. To increase your odds of recovering the system enable keyboard reset in case you can't log on and need to single user mode

machdep.kbdreset=1 # permit console CTRL-ALT-DEL to do a nice halt

If you botch an upgrade booting off of the install boot image bsd.rd will also give you a fully functional environment to make repairs from.

Now is also a good time to disable root logins

PermitRootLogin no

Services - HTTP

Getting up and going with httpd is very strait-forward

server "" {
    listen on "*" port 80

    root "htdocs/"
    location "/.well-known/acme-challenge/*" {
        root "/acme"
        request strip 2
    location "/*" {
        directory auto index

The location /.well-known/acme-challenge/* allows acme-client to validate it's identity for letsencrypt. One you have cert, https can be added

server "" {
    listen on "*" tls port 443
    tls {
        certificate "/etc/ssl/"
        key "/etc/ssl/private/"

    root "htdocs/"
    location "/*" {
        directory auto index

Services - DNS

OpenBSD ships with NSD by default. If you were using BIND the zone format is idential, you only need to enable it and add the list of zones to nsd.conf.

echo 'nsd_flags=""' >> /etc/rc.conf.local

Primary Nameserver

        name: ""
        zonefile: ""
$TTL 6h

    2     ; serial
    1h    ; refresh
    30m   ; retry
    7d    ; expiration
    1h )  ; minimum

www     IN  CNAME
vm      IN  CNAME

After restarting nsd verify like so

$ host has address mail is handled by 50

Secondary Nameservers

There are two rules to running your own nameserver

  1. There must be at least two
  2. They must not have the same IP address

This is why a secondary nameservice is required, and provides an excellent solution. Once you create an account, update your nameserver config to enable AXFR transfer so that they can keep the nameservers in sync.

    # ...
    provide-xfr: NOKEY
    provide-xfr: NOKEY
    provide-xfr: 2607:f0d0:1005:72::100 NOKEY
    provide-xfr: 2a01:4f8:d12:d01::10:100 NOKEY

Next add them as secondary authority for each zone file.



Once this works locally, use a DNS health check such as to verify that your configuration is correct.


First write your hosting provider and ask them to add a PTR record for your IP address, then add an MX record for your domain


                MX      50
selector1._domainkey IN TXT "k=rsa; t=s; p=MIGf...o8P2

Then stop sendmail and enable smtpd by following the instructions in the smtpd(8) man page.

Confirm that local relays are working by following /var/log/maillog or by running smtpd in the foreground with -dv.

Now proceed to create rules in /etc/mail/smtpd.conf for virtual domains and SMTP relay

pki cert "/etc/ssl/"
pki key "/etc/ssl/private/"

listen on lo0 port 25
listen on egress port 25
listen on egress port 465 smtps pki auth

table aliases db:/etc/mail/aliases.db
accept from any for domain "" deliver to mbox
accept for local alias <aliases> deliver to mbox

OpenSMTPD supports SSL/TLS and STARTTLS modes using the keyword https (normally port 465 ), and tls (normally port 587 ). Both of these protocols are SSL connections, but the latter switches to secure mode after connecting. If the client sends a username and password the connection is considered local and passes the last rule, allowing relay. The smtpd.conf(5) man page suggests a means of generating your own certificate.

Gmail specifically demands that messages be signed with a public key that matches the _domainkey TXT record. OpenSMTPD allows you to route messages through DKIMproxy in order to sign each e-mail

listen on localhost port 10028 tag DKIM

action "relay" relay
action relay_dkim relay host smtp://

match tag DKIM for any action "relay"

A competing or complementary technology for validating sender identity is SPF. This is much easer to configure it's simply a record that is used to specify which hosts are permitted to relay mail for a given domain.

selector1._domainkey IN TXT "k=rsa; t=s; p=MIGf...o8P2
                     IN TXT "v=spf1 mx a ptr ip4: ~all"

There are two services that were brought to my attention that can verify the SPF and DKIM signatures of a mail server. Simply send a message to and a report will be mailed back. mxtoolbox also provides a web-based diagnostic tool.

Mail Retrieval over SSL

By far the easiest way to configure pop3 access is using pop3d, which is a zero-configuration daemon. I block the plain text port using PF

block in on ! lo0 proto tcp to port pop3

Specify the keys for pop3d to use in rc.conf.local

pop3d_flags="-c /etc/ssl/ -k /etc/ssl/private/"

Preventing an Attack

If you allow Windows clients to authenticate against your server then it is also prudent to block activity that indicates that the PC has been hijacked. The following will add a client to a blacklist if there are more than 2 concurrent connections to port 465, or there are more than 8 connections made over a 30-second window

table <attacker> persist
block quick from <attacker>
pass inet proto tcp to egress:network port smtps keep state \
      (max-src-conn 2, max-src-conn-rate 8/30, overload <attacker>)

A daily cron job to list such connections is as easy as

00      1       *       *       *       /sbin/pfctl -t attacker -T show

Or a PF table can be given an expiration date for it's entries. Here we block all such hosts for 3 days

/sbin/pfctl -t attacker -T expire 259200

See Peter Hansteen's PF tutorial provides for a more complete explanation of these tactics.

Last updated on March 29, 2020