Configure stubby as DNS over HTTPS client and unbound as DNS cache for local network

Use the following configuration and set the DHCP server to hand out 10.3.0.53 and fd00:2d3f:7fc8:3::53 as DNS server.

Note: local network will not resolve with this Unbound config.

/etc/stubby/stubby.yml

listen_addresses:
  - 127.0.0.1@8053
  - 0::1@8053
root@doh:~# cat /etc/unbound/unbound.conf.d/*
server:
    # Send minimum amount of information to upstream servers to enhance
    # privacy. Only sends minimum required labels of the QNAME and sets
    # QTYPE to NS when possible.

    # See RFC 7816 "DNS Query Name Minimisation to Improve Privacy" for
    # details.

    qname-minimisation: yes
server:
    # The following line will configure unbound to perform cryptographic
    # DNSSEC validation using the root trust anchor.
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
server:
	directory: "/etc/unbound"
	username: unbound
	# make sure unbound can access entropy from inside the chroot.
	# e.g. on linux the use these commands (on BSD, devfs(8) is used):
	#      mount --bind -n /dev/random /etc/unbound/dev/random
	# and  mount --bind -n /dev/log /etc/unbound/dev/log
	# logfile: "/etc/unbound/unbound.log"  #uncomment to use logfile.
	pidfile: "/etc/unbound/unbound.pid"
	verbosity: 1
	root-hints: root.hints
	do-not-query-localhost:  no
	# listen on all interfaces, answer queries from the local subnet.
	interface: 0.0.0.0
	interface: ::0
	access-control: 10.0.0.0/8 allow
	access-control: fd00:2d3f:7fc8::/48 allow
	interface-automatic: yes
forward-zone:
  name: "."
    forward-addr: 127.0.0.1@8053
    forward-addr: ::1@8053
curl --output /etc/unbound/root.hints https://www.internic.net/domain/named.cache
root@doh:~# cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

 
 

source /etc/network/interfaces.d/*
auto eth0
iface eth0 inet static
	address 10.3.0.53/24
	gateway 10.3.0.1
iface eth0 inet6 static
        address fd00:2d3f:7fc8:3::53/64
        # use SLAAC to get global IPv6 address from the router
        # we may not enable ipv6 forwarding, otherwise SLAAC gets disabled
        autoconf 1
        accept_ra 2

Set up a simple Debian mail server with Postfix, Dovecot and opendkim

For my 19.99USD/yr VPS, the self-hosted email server solution provided by mailinabox is simply too bloated. This post will show you how to build a simple IMAP&SMTP-only mail server with Postfix and Dovecot with DKIM support. No DNS server, no fancy webmail, no CardDAV.

Highlights

  • Supports DKIM
  • Simple configuration
  • Low memory usage (483MB total 72MB used)
  • pam based authentication
  • Mailboxes (sdbox format) stored in user homes
  • Suitable for small amount of users

Configuration

DNS Records

@ 10800 IN MX 10 mail
@ 10800 IN TXT "v=spf1 mx -all"
_dmarc 10800 IN TXT "v=DMARC1; p=reject; sp=quarantine; pct=100; rua=mailto:postmaster+rua@example.com; ruf=mailto:postmaster+ruf@example.com; fo=1"
mail 10800 IN A 93.184.216.34
mail 10800 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
dkimselector._domainkey 10800 IN TXT "v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAraaOsxClVIAnIqjEjBBWTTg7x/tM1FDILMcmHgzbLZIbH7jvyjeapEtGhhwy0pOaxvv5vzZOxvDz+o2Qjbwr74h0RS0x/mexRkNMllw9Gz/pxOYLINH5VB/cgKzz+TYHTU2br8UZDZOVd3bcZSAwQv5N5N3wzlpOujEw+8cp9D+ASy+aEtFb6Ab2OxJl7juDpJd+7EGIV21+25"
	  "s4W/0a5ug1vhdVYg+TA97fcMwDSiRFHnuP/uHtlfU+hLXuHmODuHBEAI5fT6XqD+6cBa1Nyb/LjNPB5mZgtQBs5sUorh5NyTL5lrw/pg7RKFIoXwOg2iPy18FLhm6RuD1v2IPCvwIDAQAB"
smtp 10800 IN CNAME mail
imap 10800 IN CNAME mail

VPS configuration

Fresh-Clean-Brand-new Debian Buster installation REQUIRED

  1. Set up ssh access
mkdir /root/.ssh
tee -a /root/.ssh/authorized_keys << EOF
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQClNwNdN1uV/50kGEuQ7aoSIjIZAR+9zNEf/k9/rkGWFIwDxZ6jbwCp51zfZO+RuFfS3PeeChum2ukuzGgX3I0uMItxyVEdsurD7i5TmRNuT5TkigW9+LBOvIy9Qk8ueUhbkEv1P563TQVAKSMjikFYJOx/Z7dNUDcMmKap5Jauq36bE3XK++HvsyU55uN/y6D1LE8WxmkqhnmDatvY2Au6Sc6D7TxmnBbp3SZY0z9BRJ5Zf6IknC+nhqqykhU8vAdfxhpvhlCCoOf6atM+s0TqGvnNNT0L3XsQEhKcisik3mryV06IsMhTgVoWuH/cbNFGXPrsD6hNsEUL68jldj88okQuJTFCo5MROun8iRQsFJgvcEQ5MczW5DTy7lAmAWyFmmvLTW0h7yfVhgayF2JZv7+j4EaQOKaZ0iGXY0O8R2CMbz66vC2oY42fzj49jii1ozukSzyDf1Cd2XdClCOEB/bEPSk9FibovYjhxHFP11joM2zqCk6njQrXWxhfGcU= user@user1-elite-x2
EOF

cat <<EOF | tee /etc/ssh/sshd_config
PasswordAuthentication no
Port 51413
EOF

systemctl restart sshd
  1. update system
# update system
apt update && apt install -y screen && screen apt upgrade -y
# apply updates
systemctl reboot
  1. Install packages
apt install -y --no-install-recommends certbot dovecot-imapd dovecot-lmtpd postfix opendkim curl bsd-mailx opendkim-tools postfix-pcre rsync cron
  1. add mail users
groupadd -g 5000 mailusers
useradd -m -s /bin/false -g mailusers user1
useradd -m -s /bin/false -g mailusers user2
passwd user1
passwd user2
  1. generate certs
certbot --non-interactive --agree-tos -m postmaster@example.com certonly --standalone -d mail.example.com -d imap.example.com -d smtp.example.com
  1. add cert renew cron job and deploy hook
# cert renew
cat <<EOF | tee /etc/cron.daily/cert-renew
#!/bin/bash
exec certbot renew >/dev/null 2>&1
EOF
chmod +x /etc/cron.daily/cert-renew
# cert deploy hook
mkdir -p /etc/letsencrypt/renewal-hooks/post
cat <<EOF | tee /etc/letsencrypt/renewal-hooks/post/restart-mail
#!/bin/bash
systemctl restart postfix
systemctl restart dovecot
EOF
chmod +x /etc/letsencrypt/renewal-hooks/post/restart-mail
  1. generate dkimkey
# dkim key gen
cd /etc/dkimkeys
mkdir example.com
opendkim-genkey --directory=./example.com/ --domain=example.com --selector=qi2020
  1. view dkimkey dns record and add it to the DNS zone
cat dkimselector.txt
  1. fix dkim-related permissions
# fix dkimkey permission
chown opendkim:opendkim */qi2020.private
chmod 0600 */qi2020.private
  1. opendkim service config
# opendkim service config
tee -a /etc/opendkim.conf << EOF
# Automatically re-start on failures

AutoRestart yes
# limits the restarts to 10 in one hour

AutoRestartRate 10/1h
SyslogSuccess yes
Socket                  inet:8892@localhost
KeyTable file:/etc/dkimkeys/KeyTable
SigningTable refile:/etc/dkimkeys/SigningTable
EOF
tee -a /etc/dkimkeys/KeyTable << EOF
# http://www.opendkim.org/opendkim.conf.5.html

qi2020._domainkey.example.com %:qi2020:/etc/dkimkeys/%/qi2020.private
#qi2020._domainkey.example.net %:qi2020:/etc/dkimkeys/%/qi2020.private
EOF

tee -a /etc/dkimkeys/SigningTable << EOF
*@example.com qi2020._domainkey.example.com
#*@example.net qi2020._domainkey.example.net
EOF
  1. dhparams
curl https://ssl-config.mozilla.org/ffdhe2048.txt > /usr/share/dovecot/dh.pem
  1. configure dovecot
# backup original dovecot.conf
mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.or

cat <<EOF | tee /etc/dovecot/dovecot.conf

# Pigeonhole version 0.5.4 ()

# OS: Linux 4.19.0-9-amd64 x86_64 Debian 10.4 

# Hostname: mail.example.com

mail_location = sdbox:~/sdbox
mail_privileged_group = mail
namespace inbox {
  inbox = yes
  location = 
  mailbox Archive {
    auto = subscribe
    special_use = \Archive
  }
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Spam {
    special_use = \Junk
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }
  prefix = 
}
passdb {
  driver = pam
}
protocols = " imap lmtp"
service auth {
  unix_listener /var/spool/postfix/private/auth {
    group = postfix
    mode = 0600
    user = postfix
  }
}
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
}
# SSL

ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl_client_ca_dir = /etc/ssl/certs
ssl_dh = </usr/share/dovecot/dh.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_min_protocol = TLSv1.2
userdb {
  driver = passwd-file
  args = username_format=%n /etc/passwd
}
protocol lmtp {
  recipient_delimiter = +.
}
  1. configure postfix
cat <<EOF | tee /etc/postfix/main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version


# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
# fresh installs.
compatibility_level = 2



# TLS parameters
#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = mail.example.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = mail.example.com, example.com, mail.example.com, localhost.example.com, localhost
relayhost = 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
#recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
mailbox_transport = lmtp:unix:private/dovecot-lmtp
recipient_delimiter = .+

# generated 2020-07-14, Mozilla Guideline v5.4, Postfix 3.4.8, OpenSSL 1.1.1d, intermediate configuration

# https://ssl-config.mozilla.org/#server=postfix&version=3.4.8&config=intermediate&openssl=1.1.1d&guideline=5.4

smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_mandatory_ciphers = medium

# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam

# not actually 1024 bits, this applies to all DHE >= 1024 bits

smtpd_tls_dh1024_param_file = /usr/share/dovecot/dh.pem

tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
tls_preempt_cipherlist = no

virtual_alias_maps = hash:/etc/postfix/virtual

# DKIM

smtpd_milters = inet:localhost:8892
non_smtpd_milters = inet:localhost:8892

## handle other domains
# virtual_alias_domains = example.net, example.net

# block spams 
strict_rfc821_envelopes = yes
disable_vrfy_command = yes

smtpd_helo_required = yes
smtpd_recipient_restrictions =
 reject_unknown_recipient_domain,
 reject_non_fqdn_recipient,
 permit_mynetworks,
 reject_unauth_destination
 permit
EOF

tee -a /etc/postfix/master.cf << EOF
# https://github.com/mail-in-a-box/mailinabox/blob/master/setup/mail-postfix.sh
authclean unix  n       -       y       -       0       cleanup
# https://github.com/mail-in-a-box/mailinabox/blob/master/conf/postfix_outgoing_mail_header_filters
 -o header_checks=pcre:/etc/postfix/outgoing_mail_header_filters
 -o nested_header_checks=

submission inet n       -       -       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=reject_unknown_recipient_domain,reject_non_fqdn_recipient,permit_sasl_authenticated,reject
  -o cleanup_service_name=authclean
# for enabling 465 port
smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=reject_unknown_recipient_domain,reject_non_fqdn_recipient,permit_sasl_authenticated,reject
  -o cleanup_service_name=authclean

EOF

tee -a /etc/postfix/outgoing_mail_header_filters << EOF
# https://github.com/mail-in-a-box/mailinabox/blob/master/conf/postfix_ou$
# Remove the first line of the Received: header. Note that we cannot fully remove the Received: header
# /^Received: .*/ IGNORE
# because OpenDKIM requires that a header be present when signing outbound mail. The first line is
# where the user's home IP address would be.
/^\s*Received:[^\n]*(.*)/         REPLACE Received: from authenticated-user $1

# Remove other typically private information.
/^\s*User-Agent:/        IGNORE
/^\s*X-Enigmail:/        IGNORE
/^\s*X-Mailer:/          IGNORE
/^\s*X-Originating-IP:/  IGNORE
/^\s*X-Pgp-Agent:/       IGNORE

# The Mime-Version header can leak the user agent too, e.g. in Mime-Version: 1.0 (Mac OS X Mail 8.1 \(2010.6\)).
/^\s*(Mime-Version:\s*[0-9\.]+)\s.+/  REPLACE $1
EOF
  1. add postfix aliases
# virtual aliases
tee -a /etc/postfix/virtual << EOF
alias1@example.com   user1
alias2@example.com   user1
webmaster@example.com   user1
admin@example.com   user1
EOF
# user aliases
tee -a /etc/aliases << EOF
nobody: root
root: user1
EOF
# generate postmap db
newaliases
postmap /etc/postfix/virtual
  1. configure unattended-upgrades
apt install -y unattended-upgrades
dpkg-reconfigure unattended-upgrades
tee -a /etc/apt/apt.conf.d/50unattended-upgrades << EOF
Unattended-Upgrade::Mail "root";
Unattended-Upgrade::Automatic-Reboot "true";
EOF
  1. apply everything
systemctl restart opendkim
systemctl restart dovecot
systemctl restart postfix
  1. Backup
# backup
# also install rsync on the target
apt install -y rsync
ssh-keygen -t ed25519
cat .ssh/id_ed25519.pub
echo 'Host mbox.1103.example.com
     Port 54321
     User mailbackup' >> ~/.ssh/config
echo '#!/bin/bash
rsync -a --delete --quiet -e ssh /home mbox.1103.example.com:~' >> /etc/cron.hourly/mailbackup
chmod +x /etc/cron.hourly/mailbackup 
/etc/cron.hourly/mailbackup

Email client configuration

IMAP

  • Server: imap.example.com or mail.example.com or smtp.example.com
  • Security: SSL/TLS
  • Port: 993
  • Username: “user1”
  • Password: passw0rd
  • authentication method: normal password (plaintext)

SMTP

  • Server: imap.example.com or mail.example.com or smtp.example.com
  • Security: STARTTLS or SSL/TLS
  • Port: 587 or 465
  • Username: “user1”
  • Password: passw0rd
  • authentication method: normal password (plaintext)

Markdown Syntax Guide

This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.

Headings

The following HTML <h1><h6> elements represent six levels of section headings. <h1> is the highest section level while <h6> is the lowest.

H1

H2

H3

H4

H5
H6

Paragraph

Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.

Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.

Blockquotes

The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.

Blockquote without attribution

Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.

Blockquote with attribution

Don’t communicate by sharing memory, share memory by communicating.Rob Pike1

Tables

Tables aren’t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.

Name Age
Bob 27
Alice 23

Inline Markdown within tables

Inline    Markdown    In    Table
italics bold strikethrough    code

Code Blocks

Code block with backticks

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example HTML5 Document</title>
</head>
<body>
  <p>Test</p>
</body>
</html>

Code block indented with four spaces

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example HTML5 Document</title>
</head>
<body>
  <p>Test</p>
</body>
</html>

Code block with Hugo’s internal highlight shortcode

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Example HTML5 Document</title>
</head>
<body>
  <p>Test</p>
</body>
</html>

List Types

Ordered List

  1. First item
  2. Second item
  3. Third item

Unordered List

  • List item
  • Another item
  • And another item

Nested list

  • Item
  1. First Sub-item
  2. Second Sub-item

Other Elements — abbr, sub, sup, kbd, mark

GIF is a bitmap image format.

H2O

Xn + Yn = Zn

Press CTRL+ALT+Delete to end the session.

Most salamanders are nocturnal, and hunt for insects, worms, and other small creatures.


  1. The above quote is excerpted from Rob Pike’s talk during Gopherfest, November 18, 2015. ↩︎

Pirates arrrr

Piracy is typically an act of robbery or criminal violence at sea. The term can include acts committed on land, in the air, or in other major bodies of water or on a shore. It does not normally include crimes committed against persons traveling on the same vessel as the perpetrator (e.g. one passenger stealing from others on the same vessel). The term has been used throughout history to refer to raids across land borders by non-state agents.