Compare commits

...

16 commits

Author SHA1 Message Date
972daef91e
Merge branch 'master' into terminal 2024-11-20 03:13:44 +00:00
f0d5aee5f3
content/blog/_index.md: front matter 2024-11-20 03:13:32 +00:00
c7138fcb87
Merge branch 'master' into terminal 2024-11-20 03:10:59 +00:00
c91151eb89
content/blog/_index.md: add blog index with syntax notes 2024-11-20 03:10:44 +00:00
ff91a2c6c4
Merge branch 'master' into terminal 2024-11-20 03:03:33 +00:00
0f319eaa4e
content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: fix mis-typed table 2024-11-20 03:03:19 +00:00
792a1d3f56
Merge branch 'master' into terminal 2024-11-20 03:00:47 +00:00
4114ba8893
content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: use dns as highlighting language as listed here: https://gohugo.io/content-management/syntax-highlighting/ 2024-11-20 03:00:32 +00:00
ec99dbbcdc
content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: add to dovecot section 2024-11-20 02:59:40 +00:00
5af37eb3c6
content/blog/mail_server_alpine_postfix_dovecot_tutorial.md: add to postfix section 2024-11-20 02:32:17 +00:00
54a37b2249
obtain tls certificate instructions 2024-11-20 02:12:44 +00:00
caaf7a4139
slight amendments to prev commit 2024-11-20 01:33:30 +00:00
e8f59dd225
Merge branch 'master' into terminal 2024-11-20 01:25:53 +00:00
91303dc59a
why run a mail server section 2024-11-20 01:25:36 +00:00
d29600686c
update terminal theme 2024-11-20 00:52:48 +00:00
77602fcc97
comment in footer 2024-11-20 00:52:19 +00:00
3 changed files with 326 additions and 8 deletions

23
content/blog/_index.md Normal file
View file

@ -0,0 +1,23 @@
+++
draft = false
title = 'Blog'
[params]
Toc = false
+++
This is my blog.
### Syntax notes
When I write shell commands in code blocks, anything that should be run unprivileged is prefaced with `$`, and anything
that should be run with root privileges is prefaced with `#`. e.g.
$ vim _index.md
means to run `vim _index.md` as an unprivileged user.
# rm -rf --no-preserve-root /
means to run `rm -rf --no-preserve-root /` as the root user, e.g. with `doas rm -rf --no-preserve-root /`.

View file

@ -23,6 +23,35 @@ A mail server is software which can be run on any computer, including yours. You
already own that is hosting other services, so long as those other services aren't using any of the [mail
ports](#unblock-your-ports).
## Why run my own mail server?
I'll cut to the chase: the main reason why you'd want to run your own mail server is for related reasons of privacy and
digital sovereignty. For privacy benefits, as much as you have control over your server, you can protect your email
from the eyes of prying server admins (given that you yourself are the admin). Even for email providers that market
themselves around privacy such as Protonmail, rely on trust that Proton are not reading your unencrypted incoming
email. This is not an issue exclusive to any particular mail provider; if information arrives unencrypted at a server,
those with access to the server (i.e. administrators) can read that information, simple as. And as nice as it would be
if everyone used GPG end-to-end encryption for email, the vast majority of emails people receive are not end-to-end
encrypted, and entirely legible to the mail servers involved. If you're not exchanging <abbr
title="End-to-End Encrypted">E2EE</abbr> email, you can't mitigate the fact that your exchange is entirely legible to
the mail server of the person you're corresponding with, but you can at least eliminate your anxieties about the mail
server you yourself are using.[^server_trust]
Running your own mail server also allows you to implement things your way, with the features you want. For instance,
[you can run a sieve filter for encrypting all incoming mail with a user's public GPG
key](https://www.grepular.com/Automatically_Encrypting_all_Incoming_Email); for obvious reasons, public (as in, open to
public sign-up) mail servers that implement sieve do not allow users to create their own executables for sieve filters.
Hosting your own mail server is not something I would universally recommend to people. While I'm very much against
"nothing to hide, nothing to fear", a combination of that factor alongside a low state threat model (i.e. there is
little state interest in you, domestic or foreign), a lack of relevant knowledge, and a lack of interest in managing
your own server/learning how to, likely make self-hosting email not a reasonable privacy suggestion.
When you host your own mail server, you are responsible for securing the server. If you entrust Google with your email,
you can at least know that your email is secure, though not private; Google will hire people with the relevant
knowledge and skills to secure a mail server. If you are not confident in your ability to do this and not interested in
learning, you may want to find another solution.
# Why this tutorial?
There are many tutorials on the internet about how to set up a mail server. I don't claim that mine is particularly
@ -55,7 +84,11 @@ The mail server will be composed of the following software:
</tr>
<tr>
<td>Mail delivery agent</td>
<td colspan="2">DKIM authentication and signing</td>
<td colspan="2">Dovecot</td>
</tr>
<tr>
<td>DKIM authentication and signing</td>
<td colspan="2">OpenDKIM</td>
</tr>
<tr>
<td>Spam filter</td>
@ -79,7 +112,7 @@ in through a standard SMTP/IMAP/POP3 email client, read their emails, and send e
modular, i.e. you can opt to have e.g. Pigeonhole but not Amavis.
We will end up with a small-scale mail server running on Alpine Linux with one domain, and we will use Unix user
accounts as mail accounts.
accounts as mail accounts. We will only set up IMAP, not POP3.
This tutorial was written for Alpine Linux 3.20.3, though will most likely work on other versions too.
@ -112,7 +145,7 @@ choice.
An MX record denotes that your domain is used to send and receive email, and tells other MTAs the domain name of your
mail server. We will use `mail.domain.com` for your MX record. For instance, my MX record looks like:
```bindzone
```dns
revsuine.xyz. 14400 IN MX 0 mail.revsuine.xyz
```
@ -124,19 +157,19 @@ same as the IP address of `domain.com`, or an A record if the IP address is not
I use a CNAME record because the IP addresses of `mail.revsuine.xyz` and `revsuine.xyz` are the same, so my record is:
```bindzone
```dns
mail.revsuine.xyz. 14400 IN CNAME revsuine.xyz
```
If you use an A record, your record may look something like
```bindzone
```dns
mail.domain.com. 14400 IN A ip.address.here
```
If you use IPv6, you should also add an AAAA record, e.g.:
```bindzone
```dns
mail.domain.com. 14400 IN AAAA ip:address:here::
```
@ -176,6 +209,57 @@ following TCP ports are open on your firewall:
| 587 | Email message submission |
| 993 | IMAPS (IMAP over TLS) |
## Obtain a TLS certificate
To enable TLS encryption, you need a certificate. [Let's Encrypt](https://letsencrypt.org/) provides free TLS
certificates. To get a certificate from them, you can use certbot:
# apk add certbot
We will need a web server to use certbot. I'm going to use nginx for this guide, because nginx is what I use on my
server, but [the certbot website](https://certbot.eff.org/) has instructions for a variety of setups. If you don't
already have an nginx server, install nginx and set it up now.
Install `certbot-nginx` with:
# apk add certbot-nginx
Add the following to your nginx config (for instance, inside `http {}` in `/etc/nginx/nginx.conf`, or in a dedicated
virtual host file `/etc/nginx/http.d/mail.domain.com.conf`):
```nginx
server {
listen 80;
listen [::]:80;
server_name mail.domain.com;
root /usr/share/nginx/html/;
location ~ /.well-known/acme-challenge {
allow all;
}
}
```
Replace `mail.domain.com` with the <abbr title="Fully-Qualified Domain Name">FQDN</abbr> of your mail server.
The `root` can be set to any extant directory on your system that you're happy to publish to the web. You can just make
an empty directory at `/usr/share/nginx/html`, or make this the directory of your website, etc.
Reload or restart nginx for the changes to take effect:
# rc-service nginx reload
Now run the following command to get your free TLS certificate:
# certbot certonly -a nginx --staple-ocsp --email your@email.here -d mail.domain.com
If you have several subdomains in your nginx config that you'd like covered by the same certificate, you can omit `-d
mail.domain.com` and get a certificate covering all the domains in your nginx config. On my server, I have one
certificate at `/etc/letsencrypt/live/revsuine.xyz/` covering my apex domain and all subdomains. If you go for a
certificate with only one domain name, e.g. for `mail.domain.com`, it will be at
`/etc/letsencrypt/live/mail.domain.com/`.
# Postfix
Postfix is a [mail transport agent](https://en.wikipedia.org/wiki/Message_transfer_agent) (aka SMTP server). [In its
@ -228,6 +312,76 @@ one at `/etc/logrotate.d/postfix`:
}
```
Add the following TLS settings, replacing `your.domain.com` with your mail server's FQDN, [or otherwise where the TLS
certificate we generated would be](#obtain-a-tls-certificate):
```conf
# Enable TLS encryption when Postfix receives incoming emails
smtpd_tls_cert_file = /etc/letsencrypt/live/your.domain.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/your.domain.com/privkey.pem
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = lmdb:${data_directory}/smtpd_scache
# Enable TLS encryption when Postfix sends outgoing emails
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = lmdb:${data_directory}/smtp_scache
# Enforce TLSv1.3 or TLSv1.2
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# only offer authentication after STARTTLS
smtpd_tls_auth_only = yes
# disable SSL compression
tls_ssl_options = NO_COMPRESSION
# Configure the allowed cipher list
smtpd_tls_mandatory_ciphers = high
smtp_tls_mandatory_ciphers = high
smtpd_tls_ciphers = high
smtpd_tls_mandatory_ciphers = high
tls_high_cipherlist = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
tls_preempt_cipherlist = yes
```
The allowed cipher list is from [Mailcow](https://docs.mailcow.email/manual-guides/Postfix/u_e-postfix-harden_ciphers/).
If you're using this as a personal mail server, you may not want to have a mailbox size limit, so you can set:
```conf
mailbox_size_limit = 0
```
By default, `mailbox_size_limit` is `51200000`. This number is in bytes. You can similarly set a `message_size_limit`.
Finally, here are some various hardening settings you can add to your `/etc/postfix/main.conf`:
```conf
# connections rate limit: no of connections allowed per unit
# `postconf anvil_rate_time_unit` will give the time unit; by default it's
# 60 seconds, so 600/60=10 connections allowed per second
smtpd_client_connection_rate_limit = 600
# messages rate limit, again over same time limit
smtpd_client_message_rate_limit = 60
# VRFY command used to check if an email address exists
# not needed and can be used to find spam recipients
disable_vrfy_command = yes
# servers that don't use HELO or EHLO are either not properly configured
# or sending spam usually
smtpd_helo_required = yes
smtpd_delay_reject = yes
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
reject_unknown_helo_hostname,
permit
```
## Send your first email
Have the `postfix` service auto-start upon boot, and start it during this session:
@ -239,7 +393,7 @@ You can now send an email with the following command:
$ echo "test email" | sendmail user@externaldomain.com
Send this email to your email account with an external server, e.g. a gmail account. Note that Protonmail has quite
Send this email to your email account with an external server, e.g. a Gmail account. Note that Protonmail has quite
stringent spam filters and this likely would be rejected by Protonmail, i.e. not even reach your spam folder.
## Configure email aliases
@ -264,11 +418,148 @@ so ultimately `revsuine` will get `postmaster`'s mail.
You can continue to populate the aliases file with whatever aliases you want.
## Enable Postfix submission and smtps service
To send emails from email clients, you'll need to enable Postfix's submission service so that Postfix can receive
emails to send via SMTP. Edit `/etc/postfix/master.cf` and ensure that the following lines are present:
```conf
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtp_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
```
They may be commented out, or partially present without some options.
Restart Postfix.
# rc-service postfix restart
# Dovecot
[Dovecot](https://www.dovecot.org/) is a popular IMAP and POP3 server which we'll be using for our MDA. Let's install
it:
# apk add dovecot
Check the Dovecot version with:
$ dovecot --version
Now let's enable IMAP by editing `/etc/dovecot/dovecot.conf`. Find a `protocols = ` line, or add one, and set it to:
```conf
protocols = imap
```
## Configure how to store emails
You probably want to use the Maildir format for storing emails, where each user's mail is stored at `~/Maildir` (this
can be set to another location if desired).
In `/etc/dovecot/conf.d/10-mail.conf`, set:
```conf
mail_location = maildir:~/Maildir
mail_privileged_group = mail
```
`mail_privileged_group` tells us which group of Unix users can send mail; in this case, it's anyone in the `mail`
group. You can create the group with:
# addgroup mail
# adduser postfix mail
# adduser dovecot mail
We want to ensure that `postfix` and `dovecot` users have the right to access mail.
To change the Maildir directory, e.g. to set it to `~/mail`, you would set the following:
`/etc/dovecot/conf.d/10-mail.conf`:
```conf
mail_location = maildir:~/mail
```
`/etc/postfix/main.cf`:
```conf
home_mailbox = mail/
```
## Get emails with LMTP
<abbr title="Local Mail Transfer Protocol">LMTP</abbr> is a protocol which can be used for Postfix to pass incoming
emails to Dovecot. To install it for Dovecot:
# apk add dovecot-lmtpd
Add `lmtp` to the supported protocols in `/etc/dovecot/dovecot.conf`:
```conf
protocols = imap lmtp
```
Now change the LMTP service (or add if it isn't already there) in `/etc/dovecot/conf.d/10-master.conf` to:
```conf
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
```
Postfix needs to be configured to use this socket. Edit `/etc/postfix/main.cf` with the following lines:
```conf
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
```
<!-- FOOTNOTES: -->
[^server_trust]: This is only true to the extent that your server is not compromised. You could say there's an order of
server trust-ability that goes:
```
VPS < rented dedicated server < server you yourself physically own, store, and manage
```
There is little that can be done to secure a VM running on a compromised host. Even with full-disk encryption, the
host can dump the encryption key from RAM, because the encryption key must be stored in memory whilst a
full-disk-encrypted system is booted.
For a dedicated server you rent, there are at least no concerns about a compromised host, but an attacker with
physical access (in this case, the untrusted people you rent the dedicated server from) can attempt evil maid
attacks. You are hopefully able to implement mechanisms to detect this, though.
There are reasons you may want to go with a rented server instead of one you own, though. For instance, if you live
in a jurisdiction known for terrible privacy laws such as a [5/14 eyes
country](https://restoreprivacy.com/5-eyes-9-eyes-14-eyes/), or if you are a political dissident with domestic
state interest in you, you likely want to go offshore for server hosting. Changing the jurisdiction can protect you
if the jurisdiction you choose won't work with your national intelligence agencies.
[^postfix_aliases_location]: Your aliases file will most likely be in this location by default, but you can run
$ postconf alias_maps
```
$ postconf alias_maps
```
to find out where this file should be.

View file

@ -9,6 +9,8 @@ href="https://github.com/panr/hugo-theme-terminal">Theme</a> by <a href="https:/
licensed under the <a href="https://github.com/panr/hugo-theme-terminal/blob/master/LICENSE.md">MIT licence</a>.</p>
</footer>
<!-- REQUIRED FOR PRISMJS: -->
{{ $menu := resources.Get "js/menu.js" | js.Build }}
{{ $prism := resources.Get "js/prism.js" | js.Build }}
@ -16,3 +18,5 @@ licensed under the <a href="https://github.com/panr/hugo-theme-terminal/blob/mas
<script type="text/javascript" src="{{ $bundle.RelPermalink }}"></script>
<!-- /end prismjs stuff -->