Unverified Commit f5f09214 authored by Shawn Webb's avatar Shawn Webb
Browse files

Add draft "Setting up an MTA Behind Tor" article

More details to come, but this is the foundation of the Tor-ified MTA.
parent d0daf1e8
Setting up an MTA Behind Tor
============================
This article will document how to set up OpenSMTPD behind a
[fully Tor-ified network](https://github.com/lattera/articles/blob/master/infosec/tor/2017-01-14_torified_home/article.md).
Given that Tor's DNS resolver code does not support MX record lookups,
care must be taken for setting up an MTA behind a fully Tor-ified
network. OpenSMTPD was chosen because it was easy to modify to force
it to fall back to A/AAAA lookups when MX lookups failed with a DNS
result code of NOTIMP (4).
Note that as of 08 May 2018, the OpenSMTPD project is planning a
configuration file language change. The proposed change has not
landed. Once it does, this article will be updated to reflect both the
old language and new.
The reason to use an MTA behing a fully Tor-ified network is to be
able to support email behind the .onion TLD. This setup will only
allow us to send and receive email to and from the .onion TLD.
Requirements:
1. A fully Tor-ified network
1. HardenedBSD as the operating system
1. A server (or VM) running HardenedBSD behind the fully Tor-ified
network.
1. /usr/ports is empty
* Or is already pre-populated with the HardenedBSD Ports tree
Why use HardenedBSD? We get all the features of FreeBSD (ZFS, DTrace,
bhyve, and jails) with enhanced security through exploit mitigations
and system hardening. Tor has a very unique threat landscape and using
a hardened ecosystem is crucial to mitigating risks and threats.
Also note that this article reflects how I've set up my MTA. I've
included configuration files verbatim. You will need to replace the
text that refers to my .onion domain with yours.
Installation
------------
On 08 May 2018, HardenedBSD's version of OpenSMTPD just gained
[support](https://github.com/HardenedBSD/hardenedbsd-ports/commit/fbc2c8f8bb023bf91ae6dcbc5643507d3cd9c0c3)
for running an MTA behind Tor. The package repositories do not yet
contain the patch, so we will compile OpenSMTPD from ports.
We'll also install dovecot for IMAP support.
```
# pkg install -y git-lite
# mkdir -p /usr/ports
# git clone https://github.com/HardenedBSD/hardenedbsd-ports.git \
/usr/ports
# cd /usr/ports/mail/opensmtpd
# make install clean BATCH=1
# pkg install dovecot
# sysrc smtpd_enable=YES
```
Replace `/etc/mail/mailer.conf` with this:
```
# $FreeBSD$
#
# Execute the "real" sendmail program, named /usr/libexec/sendmail/sendmail
#
### smtpd: sendmail /usr/libexec/sendmail/sendmail
### smtpd: mailq /usr/libexec/sendmail/sendmail
### smtpd: newaliases /usr/libexec/sendmail/sendmail
### smtpd: hoststat /usr/libexec/sendmail/sendmail
### smtpd: purgestat /usr/libexec/sendmail/sendmail
sendmail /usr/local/sbin/smtpctl
send-mail /usr/local/sbin/smtpctl
mailq /usr/local/sbin/smtpctl
makemap /usr/local/libexec/opensmtpd/makemap
newaliases /usr/local/libexec/opensmtpd/makemap
```
Generating Cryptographic Key Material
-------------------------------------
We will want to support SSL/TLS and STARTTLS with OpenSMTPD and
Dovecot. We need to generate some crypto key material. We'll use
self-signed certificates. Note that generating the 4096-bit DH
parameters can take a substantial amount of time, so grab a pineapple
pizza and watch your favorite episode of Seinfeld. Perhaps practice
the Elaine dance a little.
```
# openssl req -x509 -new rsa:4096 \
-keyout /usr/local/etc/ssl/mta.key.pem \
-out /usr/local/etc/ssl/mta.crt.pem \
-days 3650 -nodes
# openssl dhparam -out /usr/local/etc/dovecot/dh.pem 4096
```
Tor Configuration
-----------------
We now need to add an Onion Service for our MTA. Let's add the
following lines to the torrc of our Tor-ified network:
```
HiddenServiceDir /var/db/tor/services-mta-01
HiddenServiceVersion 3
HiddenServicePort 22 192.168.254.20:22
HiddenServicePort 25 192.168.254.20:25
HiddenServicePort 80 192.168.254.20:80
HiddenServicePort 143 192.168.254.20:143
HiddenServicePort 587 192.168.254.20:587
HiddenServicePort 993 192.168.254.20:993
```
Replace 192.168.254.20 with the IP of the MTA system. You can find out
the hostname of your MTA with this command after restarting or
reloading the tor service:
```
# cat /var/db/tor/services-mta-01/hostname
```
You will use that hostname later on in this process.
OpenSMTPD Configuration
-----------------------
A basic configuration for OpenSMTPD is really simple. We'll use a
configuration file that has been only slightly modified from the
example:
Place this in `/usr/local/etc/mail/smtpd.conf`:
```
pki 3w2s7tpb5mc7ubsjjnzp4oxvqupjeoywzwdxfvfnjn3toqbuzgkn7kqd.onion certificate "/usr/local/etc/ssl/mta.crt.pem"
pki 3w2s7tpb5mc7ubsjjnzp4oxvqupjeoywzwdxfvfnjn3toqbuzgkn7kqd.onion key "/usr/local/etc/ssl/mta.key.pem"
listen on lo0
listen on igb0
listen on igb0 port 587 tls-require pki 3w2s7tpb5mc7ubsjjnzp4oxvqupjeoywzwdxfvfnjn3toqbuzgkn7kqd.onion
table aliases file:/etc/mail/aliases
accept from any for domain "3w2s7tpb5mc7ubsjjnzp4oxvqupjeoywzwdxfvfnjn3toqbuzgkn7kqd.onion" alias <aliases> deliver to maildir "~/mail"
accept for local alias <aliases> deliver to maildir "~/mail"
accept from any for domain "*.onion" relay
```
This configuration will make our MTA an open relay to other .onion
MTAs. Given the use cases for Tor, I believe this to be acceptable. We
could add authentication to the MTA, but I don't believe this to be
necessary at this point.
Dovecot Configuration
---------------------
Now that the MTA is configured, we can now set up Dovecot for IMAP
access.
```
# cp -R /usr/local/etc/dovecot/example-config/* \
/usr/local/etc/dovecot
# sysrc dovecot_enable=YES
```
Set the following variables in the following files:
1. `/usr/local/etc/dovecot/conf.d/10-auth.conf`
1. `auth_mechanisms = plain login cram-md5 digest-md5`
1. `/usr/local/etc/dovecot/conf.d/10-mail.conf`
1. `mail_location = maildir:~/mail`
1. `/usr/local/etc/dovecot/conf.d/10-ssl.conf`
1. `ssl_cert = </usr/local/etc/ssl/mta.crt.pem`
1. `ssl_key = </usr/local/etc/ssl/mta.key.pem`
1. `ssl_dh = </usr/local/etc/dovecot/dh.pem`
Some files have weird syntax that is hard to describe in the above
numbered list format. The following files are pasted verbatim. The
first commented line contains the filename.
```
# /usr/local/etc/dovecot/conf.d/10-master.conf
#default_process_limit = 100
#default_client_limit = 1000
# Default VSZ (virtual memory size) limit for service processes. This is mainly
# intended to catch and kill processes that leak memory before they eat up
# everything.
#default_vsz_limit = 256M
# Login user is internally used by login processes. This is the most untrusted
# user in Dovecot system. It shouldn't have access to anything at all.
#default_login_user = dovenull
# Internal user is used by unprivileged processes. It should be separate from
# login user, so that login processes can't disturb other processes.
#default_internal_user = dovecot
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
ssl = yes
}
# Number of connections to handle before starting a new process. Typically
# the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0
# is faster. <doc/wiki/LoginProcess.txt>
#service_count = 1
# Number of processes to always keep waiting for more connections.
#process_min_avail = 0
# If you set service_count=0, you probably need to grow this.
#vsz_limit = $default_vsz_limit
}
service pop3-login {
inet_listener pop3 {
#port = 110
}
inet_listener pop3s {
#port = 995
#ssl = yes
}
}
service submission-login {
inet_listener submission {
#port = 587
}
}
service lmtp {
unix_listener lmtp {
#mode = 0666
}
# Create inet listener only if you can't use the above UNIX socket
#inet_listener lmtp {
# Avoid making LMTP visible for the entire internet
#address =
#port =
#}
}
service imap {
# Most of the memory goes to mmap()ing files. You may need to increase this
# limit if you have huge mailboxes.
#vsz_limit = $default_vsz_limit
# Max. number of IMAP processes (connections)
#process_limit = 1024
}
service pop3 {
# Max. number of POP3 processes (connections)
#process_limit = 1024
}
service submission {
# Max. number of SMTP Submission processes (connections)
#process_limit = 1024
}
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
# full permissions to this socket are able to get a list of all usernames and
# get the results of everyone's userdb lookups.
#
# The default 0666 mode allows anyone to connect to the socket, but the
# userdb lookups will succeed only if the userdb returns an "uid" field that
# matches the caller process's UID. Also if caller's uid or gid matches the
# socket's uid or gid the lookup succeeds. Anything else causes a failure.
#
# To give the caller full permissions to lookup all users, set the mode to
# something else than 0666 and Dovecot lets the kernel enforce the
# permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb {
#mode = 0666
#user =
#group =
}
# Postfix smtp-auth
#unix_listener /var/spool/postfix/private/auth {
# mode = 0666
#}
# Auth process is run as this user.
#user = $default_internal_user
}
service auth-worker {
# Auth worker process is run as root by default, so that it can access
# /etc/shadow. If this isn't necessary, the user should be changed to
# $default_internal_user.
#user = root
}
service dict {
# If dict proxy is used, mail processes should have access to its socket.
# For example: mode=0660, group=vmail and global mail_access_groups=vmail
unix_listener dict {
#mode = 0600
#user =
#group =
}
}
```
Testing your configuration
--------------------------
Use your favorite MUA (mutt, neomutt, thunderbird, etc.) to connect to
your MTA. Feel free to send me an email at
shawn@3w2s7tpb5mc7ubsjjnzp4oxvqupjeoywzwdxfvfnjn3toqbuzgkn7kqd.onion
and I'll do my best to respond when I can.
##
## Authentication processes
##
# Disable LOGIN command and all other plaintext authentications unless
# SSL/TLS is used (LOGINDISABLED capability). Note that if the remote IP
# matches the local IP (ie. you're connecting from the same computer), the
# connection is considered secure and plaintext authentication is allowed.
# See also ssl=required setting.
disable_plaintext_auth = no
# Authentication cache size (e.g. 10M). 0 means it's disabled. Note that
# bsdauth, PAM and vpopmail require cache_key to be set for caching to be used.
#auth_cache_size = 0
# Time to live for cached data. After TTL expires the cached record is no
# longer used, *except* if the main database lookup returns internal failure.
# We also try to handle password changes automatically: If user's previous
# authentication was successful, but this one wasn't, the cache isn't used.
# For now this works only with plaintext authentication.
#auth_cache_ttl = 1 hour
# TTL for negative hits (user not found, password mismatch).
# 0 disables caching them completely.
#auth_cache_negative_ttl = 1 hour
# Space separated list of realms for SASL authentication mechanisms that need
# them. You can leave it empty if you don't want to support multiple realms.
# Many clients simply use the first one listed here, so keep the default realm
# first.
#auth_realms =
# Default realm/domain to use if none was specified. This is used for both
# SASL realms and appending @domain to username in plaintext logins.
#auth_default_realm =
# List of allowed characters in username. If the user-given username contains
# a character not listed in here, the login automatically fails. This is just
# an extra check to make sure user can't exploit any potential quote escaping
# vulnerabilities with SQL/LDAP databases. If you want to allow all characters,
# set this value to empty.
#auth_username_chars = abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@
# Username character translations before it's looked up from databases. The
# value contains series of from -> to characters. For example "#@/@" means
# that '#' and '/' characters are translated to '@'.
#auth_username_translation =
# Username formatting before it's looked up from databases. You can use
# the standard variables here, eg. %Lu would lowercase the username, %n would
# drop away the domain if it was given, or "%n-AT-%d" would change the '@' into
# "-AT-". This translation is done after auth_username_translation changes.
#auth_username_format = %Lu
# If you want to allow master users to log in by specifying the master
# username within the normal username string (ie. not using SASL mechanism's
# support for it), you can specify the separator character here. The format
# is then <username><separator><master username>. UW-IMAP uses "*" as the
# separator, so that could be a good choice.
#auth_master_user_separator =
# Username to use for users logging in with ANONYMOUS SASL mechanism
#auth_anonymous_username = anonymous
# Maximum number of dovecot-auth worker processes. They're used to execute
# blocking passdb and userdb queries (eg. MySQL and PAM). They're
# automatically created and destroyed as needed.
#auth_worker_max_count = 30
# Host name to use in GSSAPI principal names. The default is to use the
# name returned by gethostname(). Use "$ALL" (with quotes) to allow all keytab
# entries.
#auth_gssapi_hostname =
# Kerberos keytab to use for the GSSAPI mechanism. Will use the system
# default (usually /etc/krb5.keytab) if not specified. You may need to change
# the auth service to run as root to be able to read this file.
#auth_krb5_keytab =
# Do NTLM and GSS-SPNEGO authentication using Samba's winbind daemon and
# ntlm_auth helper. <doc/wiki/Authentication/Mechanisms/Winbind.txt>
#auth_use_winbind = no
# Path for Samba's ntlm_auth helper binary.
#auth_winbind_helper_path = /usr/bin/ntlm_auth
# Time to delay before replying to failed authentications.
#auth_failure_delay = 2 secs
# Require a valid SSL client certificate or the authentication fails.
#auth_ssl_require_client_cert = no
# Take the username from client's SSL certificate, using
# X509_NAME_get_text_by_NID() which returns the subject's DN's
# CommonName.
#auth_ssl_username_from_cert = no
# Space separated list of wanted authentication mechanisms:
# plain login digest-md5 cram-md5 ntlm rpa apop anonymous gssapi otp skey
# gss-spnego
# NOTE: See also disable_plaintext_auth setting.
auth_mechanisms = plain login cram-md5 digest-md5
##
## Password and user databases
##
#
# Password database is used to verify user's password (and nothing more).
# You can have multiple passdbs and userdbs. This is useful if you want to
# allow both system users (/etc/passwd) and virtual users to login without
# duplicating the system users into virtual database.
#
# <doc/wiki/PasswordDatabase.txt>
#
# User database specifies where mails are located and what user/group IDs
# own them. For single-UID configuration use "static" userdb.
#
# <doc/wiki/UserDatabase.txt>
#!include auth-deny.conf.ext
#!include auth-master.conf.ext
!include auth-system.conf.ext
#!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext
##
## Director-specific settings.
##
# Director can be used by Dovecot proxy to keep a temporary user -> mail server
# mapping. As long as user has simultaneous connections, the user is always
# redirected to the same server. Each proxy server is running its own director
# process, and the directors are communicating the state to each others.
# Directors are mainly useful with NFS-like setups.
# List of IPs or hostnames to all director servers, including ourself.
# Ports can be specified as ip:port. The default port is the same as
# what director service's inet_listener is using.
#director_servers =
# List of IPs or hostnames to all backend mail servers. Ranges are allowed
# too, like 10.0.0.10-10.0.0.30.
#director_mail_servers =
# How long to redirect users to a specific server after it no longer has
# any connections.
#director_user_expire = 15 min
# How the username is translated before being hashed. Useful values include
# %Ln if user can log in with or without @domain, %Ld if mailboxes are shared
# within domain.
#director_username_hash = %Lu
# To enable director service, uncomment the modes and assign a port.
service director {
unix_listener login/director {
#mode = 0666
}
fifo_listener login/proxy-notify {
#mode = 0666
}
unix_listener director-userdb {
#mode = 0600
}
inet_listener {
#port =
}
}
# Enable director for the wanted login services by telling them to
# connect to director socket instead of the default login socket:
service imap-login {
#executable = imap-login director
}
service pop3-login {
#executable = pop3-login director
}
service submission-login {
#executable = submission-login director
}
# Enable director for LMTP proxying:
protocol lmtp {
#auth_socket_path = director-userdb
}
##
## Log destination.
##
# Log file to use for error messages. "syslog" logs to syslog,
# /dev/stderr logs to stderr.
#log_path = syslog
# Log file to use for informational messages. Defaults to log_path.
#info_log_path =
# Log file to use for debug messages. Defaults to info_log_path.
#debug_log_path =
# Syslog facility to use if you're logging to syslog. Usually if you don't
# want to use "mail", you'll use local0..local7. Also other standard
# facilities are supported.
#syslog_facility = mail
##
## Logging verbosity and debugging.
##
# Log unsuccessful authentication attempts and the reasons why they failed.
#auth_verbose = no
# In case of password mismatches, log the attempted password. Valid values are
# no, plain and sha1. sha1 can be useful for detecting brute force password
# attempts vs. user simply trying the same password over and over again.
# You can also truncate the value to n chars by appending ":n" (e.g. sha1:6).
#auth_verbose_passwords = no
# Even more verbose logging for debugging purposes. Shows for example SQL
# queries.
#auth_debug = no
# In case of password mismatches, log the passwords and used scheme so the
# problem can be debugged. Enabling this also enables auth_debug.
#auth_debug_passwords = no
# Enable mail process debugging. This can help you figure out why Dovecot
# isn't finding your mails.
#mail_debug = no
# Show protocol level SSL errors.
#verbose_ssl = no
# mail_log plugin provides more event logging for mail processes.
plugin {
# Events to log. Also available: flag_change append
#mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename
# Available fields: uid, box, msgid, from, subject, size, vsize, flags
# size and vsize are available only for expunge and copy events.
#mail_log_fields = uid box msgid size
}
##
## Log formatting.
##
# Prefix for each line written to log file. % codes are in strftime(3)
# format.
#log_timestamp = "%b %d %H:%M:%S "
# Space-separated list of elements we want to log. The elements which have
# a non-empty variable value are joined together to form a comma-separated
# string.
#login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c
# Login log format. %s contains login_log_format_elements string, %$ contains
# the data we want to log.
#login_log_format = %$: %s
# Log prefix for mail processes. See doc/wiki/Variables.txt for list of
# possible variables you can use.
#mail_log_prefix = "%s(%u)<%{pid}><%{session}>: "
# Format to use for logging mail deliveries:
# %$ - Delivery status message (e.g. "saved to INBOX")
# %m / %{msgid} - Message-ID
# %s / %{subject} - Subject
# %f / %{from} - From address
# %p / %{size} - Physical size
# %w / %{vsize} - Virtual size
# %e / %{from_envelope} - MAIL FROM envelope
# %{to_envelope} - RCPT TO envelope
# %{delivery_time} - How many milliseconds it took to deliver the mail
# %{session_time} - How long LMTP session took, not including delivery_time
# %{storage_id} - Backend-specific ID for mail, e.g. Maildir filename
#deliver_log_format = msgid=%m: %$
##
## Mailbox locations and namespaces
##
# Location for users' mailboxes. The default is empty, which means that Dovecot
# tries to find the mailboxes automatically. This won't work if the user
# doesn't yet have any mail, so you should explicitly tell Dovecot the full
# location.
#
# If you're using mbox, giving a path to the INBOX file (eg. /var/mail/%u)
# isn't enough. You'll also need to tell Dovecot where the other mailboxes are
# kept. This is called the "root mail directory", and it must be the first
# path given in the mail_location setting.
#
# There are a few special variables you can use, eg.:
#