Debian + Postfix + Dovecot + Multidomain + SSL + IPv6 + OpenVPN + Multi-interfaces + SpamAssassin-learn + Bind

This article is about how to set up a modern mail server.
Postfix + Dovecot. SPF + DKIM + rDNS. with IPv6.
With TSL encryption. With support for multiple domains - the part with a real SSL certificate.
With anti-spam protection and a high anti-spam rating from other mail servers.
With support for multiple physical interfaces.
With OpenVPN, the connection to which is via IPv4, and which gives IPv6.

If you do not want to learn all these technologies, but want to set up such a server, then this article is for you.

This article does not attempt to explain every detail. The explanation goes to what is not set up standardly or is important from the point of view of the consumer.

The motivation to set up a mail server is my old dream. It may sound silly, but IMHO, this is much better than dreaming about a new car of your favorite brand.

The motivation to configure IPv6 is two. An IT specialist needs to learn new technologies constantly in order to survive. I would like to make my modest contribution to the fight against censorship.

The motivation for configuring OpenVPN is just to make IPv6 work on the local machine.
The motivation for setting up multiple physical interfaces is that on my server I have one “slow but unlimited” interface and the other “fast, but with a tariff”.

The motivation for setting up the Bind settings is that my ISP provides an unstable DNS server, and google sometimes crashes too. I want a stable DNS server for personal use.

The motivation to write the article is that the draft was written 10 months ago, and I have already looked into it twice. Even if the author needs it regularly, then there is a high probability that others will need it.

There is no universal solution for the mail server. But I will try to write something like “do this and then, when everything works as it should, throw out the excess.”

There is a Colocation server from tech.ru. It is possible to compare with OVH, Hetzner, AWS. To solve this problem, cooperation with tech.ru will be much more effective.

The server has Debian 9 installed.

Server has 2 interfaces `eno1` and `eno2`. The first is unlimited, and the second is fast, respectively.

There are 3 static IP addresses, XX.XX.XX.X0 and XX.XX.XX.X1 and XX.XX.XX.X2 on the `eno1` interface and XX.XX.XX.X5 on the `eno2` interface.

Available XXXX:XXXX:XXXX:XXXX::/64 a pool of IPv6 addresses that are assigned to the `eno1` interface and from it XXXX:XXXX:XXXX:XXXX:1:2::/96 was assigned to `eno2` at my request.

There are 3 domains `domain1.com`, `domain2.com`, `domain3.com`. There is an SSL certificate for `domain1.com` and `domain3.com`.

I have a google account to which I want to link a mailbox `[email protected]` (receiving mail and sending mail directly from the gmail interface).
Must have a mailbox `[email protected]`, a copy of the mail from which I want to see in my gmail. And it's rare to be able to send something on behalf of `[email protected]` via the web interface.

Must have a mailbox `[email protected]`, which Ivanov will use from his iPhone.

Emails sent must comply with all modern requirements for anti-spam.
Should be the highest level of encryption provided in public networks.
There must be IPv6 support for both sending and receiving emails.
Should be SpamAssassin, which will never delete emails. And it will either bounce or skip or send to the IMAP Spam folder.
SpamAssassin auto-learning must be configured: if I move the letter to the Spam folder, it will learn from this; if I move a letter from the Spam folder, it will learn from it. SpamAssassin training results - should influence the hitting of the letter in the Spam folder.
PHP scripts should be able to send mail on behalf of any domain on a given server.
There must be an openvpn service, with the ability to use IPv6 on a client that does not have IPv6.

First you need to configure interfaces and routing, including IPv6.
Then you will need to configure OpenVPN, which will connect via IPv4 and provide the client with a static-real IPv6 address. This client will have access to all IPv6 services on the server and access to any IPv6 resources on the Internet.
Then you will need to configure Postfix to send emails + SPF + DKIM + rDNS and other little things like that.
Then you will need to configure Dovecot and configure Multidomain.
Then you will need to configure SpamAssassin and set up training.
Finally, install Bind.

============= Multi-interfaces =============

To configure interfaces, you need to register this in "/etc/network/interfaces".

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eno1
iface eno1 inet static
        address XX.XX.XX.X0/24
        gateway XX.XX.XX.1
        dns-nameservers 127.0.0.1 213.248.1.6
        post-up ip route add XX.XX.XX.0/24 dev eno1 src XX.XX.XX.X0 table eno1t
        post-up ip route add default via XX.XX.XX.1 table eno1t
        post-up ip rule add table eno1t from XX.XX.XX.X0
        post-up ip rule add table eno1t to XX.XX.XX.X0

auto eno1:1
iface eno1:1 inet static
address XX.XX.XX.X1
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X1
        post-up ip rule add table eno1t to XX.XX.XX.X1
        post-up   ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t
        post-down ip route del 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t

auto eno1:2
iface eno1:2 inet static
address XX.XX.XX.X2
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X2
        post-up ip rule add table eno1t to XX.XX.XX.X2

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
        gateway XXXX:XXXX:XXXX:XXXX::1
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:2/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:1:1:2/64 dev $IFACE

# The secondary network interface
allow-hotplug eno2
iface eno2 inet static
        address XX.XX.XX.X5
        netmask 255.255.255.0
        post-up   ip route add XX.XX.XX.0/24 dev eno2 src XX.XX.XX.X5 table eno2t
        post-up   ip route add default via XX.XX.XX.1 table eno2t
        post-up   ip rule add table eno2t from XX.XX.XX.X5
        post-up   ip rule add table eno2t to XX.XX.XX.X5
        post-up   ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X5 table eno2t
        post-down ip route del 10.8.0.0/24 dev tun0 src XX.XX.XX.X5 table eno2t

iface eno2 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:2::/96
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:2:1:1/64 dev $IFACE
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:2:1:2/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:2:1:1/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:2:1:2/64 dev $IFACE

# OpenVPN network
iface tun0 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:3::/80

These settings can be applied on any server in tech.ru (with a little coordination with support) and it will immediately work as it should.

If the experience of setting up similar things for Hetzner, OVH is different there. More difficult.

eno1 is the name of network card #1 (slow but unlimited).
eno2 is the name of network card #2 (fast, but with a tariff).
tun0 is the name of the virtual NIC from OpenVPN.
XX.XX.XX.X0 - IPv4 #1 on eno1.
XX.XX.XX.X1 - IPv4 #2 on eno1.
XX.XX.XX.X2 - IPv4 #3 on eno1.
XX.XX.XX.X5 - IPv4 #1 on eno2.
XX.XX.XX.1 - IPv4 gateway.
XXXX:XXXX:XXXX:XXXX::/64 - IPv6 for the entire server.
XXXX:XXXX:XXXX:XXXX:1:2::/96 - IPv6 for eno2, everything else goes to eno1 from the outside.
XXXX:XXXX:XXXX:XXXX::1 - IPv6 gateway (it is worth noting that here you can / need to do it differently. Specify the IPv6 switch).
dns-nameservers - 127.0.0.1 are specified (because bind is installed locally) and 213.248.1.6 (this is from tech.ru).

"table eno1t" and "table eno2t" - the meaning of these route-rules is that the traffic entered through eno1 -> would leave through it, and the traffic entered through eno2 -> would leave through it. And also connections at the initiative of the server would leave through eno1.

ip route add default via XX.XX.XX.1 table eno1t

With this command, we set that any incomprehensible traffic that falls under any rule that has “table eno1t” marked -> forward to the eno1 interface.

ip route add XX.XX.XX.0/24 dev eno1 src XX.XX.XX.X0 table eno1t

With this command, we specify that any traffic initiated by the server is directed to the eno1 interface.

ip rule add table eno1t from XX.XX.XX.X0
ip rule add table eno1t to XX.XX.XX.X0

With this command, we set the traffic marking rules themselves.

auto eno1:2
iface eno1:2 inet static
address XX.XX.XX.X2
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X2
        post-up ip rule add table eno1t to XX.XX.XX.X2

This block specifies a second IPv4 for the eno1 interface.

ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t

With this command, we set the route from OpenVPN clients to local IPv4 except for XX.XX.XX.X0.
Why this command is enough for all IPv4 - I still do not understand.

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
        gateway XXXX:XXXX:XXXX:XXXX::1

We set the address for the interface itself. The server will use it as the "outgoing" address. No more will be used.

Why is ":1:1::" so complicated? For OpenVPN to work correctly and only for this. More on this later.

On the subject of gateway - this works fine. But according to the correct one, here you need to specify the IPv6 of the switch to which the server is connected.

However, for some reason IPv6 stops working if I do this. Probably, these are some troubles of tech.ru.

ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE

This is adding an IPv6 address to an interface. If you need a hundred addresses, then a hundred lines in this file.

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
...
iface eno2 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:2::/96
...
iface tun0 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:3::/80

Marked the addresses and subnets of all interfaces to make it clear.
eno1 - must be "/64" - because this is our entire pool of addresses.
tun0 - the subnet must be greater than eno1. Otherwise, you will not be able to configure IPv6 gateway for OpenVPN clients.
eno2 - the subnet must be greater than tun0. Otherwise, OpenVPN clients will not be able to get to IPv6 local addresses.
For clarity, I chose the subnet step 16, but if you wish, you can even do a “1” step.
Accordingly, 64 + 16 = 80, and 80 + 16 = 96.

For even more clarity:
XXXX:XXXX:XXXX:XXXX:1:1:YYYY:YYYY are the addresses that should be assigned to specific sites or services on interface eno1.
XXXX:XXXX:XXXX:XXXX:1:2:YYYY:YYYY are the addresses that should be assigned to specific sites or services on interface eno2.
XXXX:XXXX:XXXX:XXXX:1:3:YYYY:YYYY are the addresses that should be assigned to OpenVPN clients or used as OpenVPN business addresses.

To configure the network, it should be possible to restart the server.
IPv4 changes are picked up during execution (be sure to wrap it in screen - otherwise this command will simply drop the network on the server):

/etc/init.d/networking restart

In the file "/etc/iproute2/rt_tables" add to the end:

100 eno1t
101 eno2t

Without this, you cannot use custom tables in the "/etc/network/interfaces" file.
Digits must be unique and less than 65535.

IPv6 changes are easily changed without a reboot, but for this you need to learn at least three commands:

ip -6 addr ...
ip -6 route ...
ip -6 neigh ...

Setting "/etc/sysctl.conf"

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward = 1

# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects = 0

# For receiving ARP replies
net.ipv4.conf.all.arp_filter = 0
net.ipv4.conf.default.arp_filter = 0

# For sending ARP
net.ipv4.conf.all.arp_announce = 0
net.ipv4.conf.default.arp_announce = 0

# Enable IPv6
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0

# IPv6 configuration
net.ipv6.conf.all.autoconf = 1
net.ipv6.conf.all.accept_ra = 0

# For OpenVPN
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1

# For nginx on boot
net.ipv6.ip_nonlocal_bind = 1

These are my server's "sysctl" settings. I'll point out something important.

net.ipv4.ip_forward = 1

Without this, OpenVPN will not work in any way.

net.ipv6.ip_nonlocal_bind = 1

Anyone who tries to bind IPv6 (eg nginx) right after the interface has gone up will get an error. That such an address is not available.

To avoid such a situation, such a setting is made.

net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1

Without these settings, IPv6 traffic from the OpenVPN client does not go out into the world.

The other settings are either irrelevant or I don't remember why.
But just in case, I leave it "as is".

In order for the changes to this file to be picked up without restarting the server, you need to run the command:

sysctl -p

More details about "table" rules: habr.com/post/108690

============= OpenVPN ==============

OpenVPN IPv4 doesn't work without iptables.

I have iptables like this for VPN:

iptables -A INPUT -p udp -s YY.YY.YY.YY --dport 1194 -j ACCEPT
iptables -A FORWARD -i tun0 -o eno1 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j SNAT --to-source XX.XX.XX.X0
##iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j MASQUERADE
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -j DROP
iptables -A FORWARD -p udp --dport 1194 -j DROP

YY.YY.YY.YY is my static IPv4 address of the local machine.
10.8.0.0/24 - IPv4 openvpn network. IPv4 addresses for openvpn clients.
The order of the rules is important.

iptables -A INPUT -p udp -s YY.YY.YY.YY --dport 1194 -j ACCEPT
iptables -A FORWARD -i tun0 -o eno1 -j ACCEPT
...
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -j DROP
iptables -A FORWARD -p udp --dport 1194 -j DROP

This is a restriction so that only I can use OpenVPN from my static IP.

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j SNAT --to-source XX.XX.XX.X0
  -- или --
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j MASQUERADE

To forward IPv4 packets between OpenVPN clients and the Internet, you need to register one of these commands.

For different cases, one of the options is not suitable.
Both commands are suitable for my case.
After reading the documentation, I chose the first option, because it eats less CPU.

In order for all iptables settings to be picked up after reboot, you need to save them somewhere.

iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

These names were not chosen by chance. They are used by the "iptables-persistent" package.

apt-get install iptables-persistent

Installing the main OpenVPN package:

apt-get install openvpn easy-rsa

Set up a template for certificates (substitute your own values):

make-cadir ~/openvpn-ca
cd ~/openvpn-ca
ln -s openssl-1.0.0.cnf openssl.cnf

Let's edit the certificate template settings:

mcedit vars

...
# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="RU"
export KEY_PROVINCE="Krasnodar"
export KEY_CITY="Dinskaya"
export KEY_ORG="Own"
export KEY_EMAIL="[email protected]"
export KEY_OU="VPN"

# X509 Subject Field
export KEY_NAME="server"
...

Create a server certificate:

cd ~/openvpn-ca
source vars
./clean-all
./build-ca
./build-key-server server
./build-dh
openvpn --genkey --secret keys/ta.key

Let's prepare the ability to create the final "client-name.opvn" files:

mkdir -p ~/client-configs/files
chmod 700 ~/client-configs/files
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf
mcedit ~/client-configs/base.conf

# Client mode
client

# Interface tunnel type
dev tun

# TCP protocol
proto tcp-client

# Address/Port of VPN server
remote XX.XX.XX.X0 1194

# Don't bind to local port/address
nobind

# Don't need to re-read keys and re-create tun at restart
persist-key
persist-tun

# Remote peer must have a signed certificate
remote-cert-tls server
ns-cert-type server

# Enable compression
comp-lzo

# Custom
ns-cert-type server
tls-auth ta.key 1
cipher DES-EDE3-CBC

Let's prepare a script that will merge all files into a single opvn file.

mcedit ~/client-configs/make_config.sh
chmod 700 ~/client-configs/make_config.sh

#!/bin/bash

# First argument: Client identifier

KEY_DIR=~/openvpn-ca/keys
OUTPUT_DIR=~/client-configs/files
BASE_CONFIG=~/client-configs/base.conf

cat ${BASE_CONFIG} 
    <(echo -e '<ca>') 
    ${KEY_DIR}/ca.crt 
    <(echo -e '</ca>n<cert>') 
    ${KEY_DIR}/.crt 
    <(echo -e '</cert>n<key>') 
    ${KEY_DIR}/.key 
    <(echo -e '</key>n<tls-auth>') 
    ${KEY_DIR}/ta.key 
    <(echo -e '</tls-auth>') 
    > ${OUTPUT_DIR}/.ovpn

Create the first OpenVPN client:

cd ~/openvpn-ca
source vars
./build-key client-name
cd ~/client-configs
./make_config.sh client-name

The file "~/client-configs/files/client-name.ovpn" is sent to the device to the client.

For iOS clients, you will need to do the trick:
The content of the "tls-auth" tag must be without comments.
And also put "key-direction 1" immediately before the "tls-auth" tag.

Set up the OpenVPN server config:

cd ~/openvpn-ca/keys
cp ca.crt ca.key server.crt server.key ta.key dh2048.pem /etc/openvpn
gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz | tee /etc/openvpn/server.conf
mcedit /etc/openvpn/server.conf

# Listen port
port 1194

# Protocol
proto tcp-server

# IP tunnel
dev tun0
tun-ipv6
push tun-ipv6

# Master certificate
ca ca.crt

# Server certificate
cert server.crt

# Server private key
key server.key

# Diffie-Hellman parameters
dh dh2048.pem

# Allow clients to communicate with each other
client-to-client

# Client config dir
client-config-dir /etc/openvpn/ccd

# Run client-specific script on connection and disconnection
script-security 2
client-connect "/usr/bin/sudo -u root /etc/openvpn/server-clientconnect.sh"
client-disconnect "/usr/bin/sudo -u root /etc/openvpn/server-clientdisconnect.sh"

# Server mode and client subnets
server 10.8.0.0 255.255.255.0
server-ipv6 XXXX:XXXX:XXXX:XXXX:1:3::/80
topology subnet

# IPv6 routes
push "route-ipv6 XXXX:XXXX:XXXX:XXXX::/64"
push "route-ipv6 2000::/3"

# DNS (for Windows)
# These are OpenDNS
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

# Configure all clients to redirect their default network gateway through the VPN
push "redirect-gateway def1 bypass-dhcp"
push "redirect-gateway ipv6" #For iOS

# Don't need to re-read keys and re-create tun at restart
persist-key
persist-tun

# Ping every 10s. Timeout of 120s.
keepalive 10 120

# Enable compression
comp-lzo

# User and group
user vpn
group vpn

# Log a short status
status openvpn-status.log

# Logging verbosity
##verb 4

# Custom config
tls-auth ta.key 0
cipher DES-EDE3-CBC

This is necessary in order to set a static address for each client (not necessary, but I use):

# Client config dir
client-config-dir /etc/openvpn/ccd

The most difficult and key detail.

Unfortunately, OpenVPN does not yet know how to configure IPv6 gateway for clients on its own.
You have to "manually" forward this for each client.

# Run client-specific script on connection and disconnection
script-security 2
client-connect "/usr/bin/sudo -u root /etc/openvpn/server-clientconnect.sh"
client-disconnect "/usr/bin/sudo -u root /etc/openvpn/server-clientdisconnect.sh"

File "/etc/openvpn/server-clientconnect.sh":

#!/bin/sh

# Check client variables
if [ -z "$ifconfig_pool_remote_ip" ] || [ -z "$common_name" ]; then
        echo "Missing environment variable."
        exit 1
fi

# Load server variables
. /etc/openvpn/variables

ipv6=""

# Find out if there is a specific config with fixed IPv6 for this client
if [ -f "/etc/openvpn/ccd/$common_name" ]; then
        # Get fixed IPv6 from client config file
        ipv6=$(sed -nr 's/^.*ifconfig-ipv6-push[ t]+([0-9a-fA-F:]+).*$/1/p' "/etc/openvpn/ccd/$common_name")
        echo $ipv6
fi

# Get IPv6 from IPv4
if [ -z "$ipv6" ]; then
        ipp=$(echo "$ifconfig_pool_remote_ip" | cut -d. -f4)
        if ! [ "$ipp" -ge 2 -a "$ipp" -le 254 ] 2>/dev/null; then
                echo "Invalid IPv4 part."
                exit 1
        fi
        hexipp=$(printf '%x' $ipp)
        ipv6="$prefix$hexipp"
fi

# Create proxy rule
/sbin/ip -6 neigh add proxy $ipv6 dev eno1

File "/etc/openvpn/server-clientdisconnect.sh":

#!/bin/sh

# Check client variables
if [ -z "$ifconfig_pool_remote_ip" ] || [ -z "$common_name" ]; then
        echo "Missing environment variable."
        exit 1
fi

# Load server variables
. /etc/openvpn/variables

ipv6=""

# Find out if there is a specific config with fixed IPv6 for this client
if [ -f "/etc/openvpn/ccd/$common_name" ]; then
        # Get fixed IPv6 from client config file
        ipv6=$(sed -nr 's/^.*ifconfig-ipv6-push[ t]+([0-9a-fA-F:]+).*$/1/p' "/etc/openvpn/ccd/$common_name")
fi

# Get IPv6 from IPv4
if [ -z "$ipv6" ]; then
        ipp=$(echo "$ifconfig_pool_remote_ip" | cut -d. -f4)
        if ! [ "$ipp" -ge 2 -a "$ipp" -le 254 ] 2>/dev/null; then
                echo "Invalid IPv4 part."
                exit 1
        fi
        hexipp=$(printf '%x' $ipp)
        ipv6="$prefix$hexipp"
fi

# Delete proxy rule
/sbin/ip -6 neigh del proxy $ipv6 dev eno1

Both scripts use the "/etc/openvpn/variables" file:

# Subnet
prefix=XXXX:XXXX:XXXX:XXXX:2:
# netmask
prefixlen=112

Why it is so written here - I find it difficult to remember.

Now it looks strange netmask = 112 (it should be 96 right there).
And the prefix is ​​weird, doesn't match the tun0 network.
But okay, I'll leave it as is.

cipher DES-EDE3-CBC

This is not for everyone - I chose this method of encrypting the connection.

Learn more about configuring OpenVPN IPv4.

Learn more about configuring OpenVPN IPv6.

============ Postfix ==============

Installing the main package:

apt-get install postfix

When installing, select "internet-site".

My "/etc/postfix/main.cf" looks like this:

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

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

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/domain1.com.2018.chained.crt
smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_bind_address = XX.XX.XX.X0
smtp_bind_address6 = XXXX:XXXX:XXXX:XXXX:1:1:1:1

smtp_tls_security_level = may
smtp_tls_ciphers = export
smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_loglevel = 1

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = domain1.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = domain1.com
mydestination = 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 = ipv4

internal_mail_filter_classes = bounce

# Storage type
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

# SMTP-Auth settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions =
        permit_sasl_authenticated,
        permit_mynetworks,
        #reject_invalid_hostname,
        #reject_unknown_recipient_domain,
        reject_unauth_destination,
        reject_rbl_client sbl.spamhaus.org,
        check_policy_service unix:private/policyd-spf

smtpd_helo_restrictions =
        #reject_invalid_helo_hostname,
        #reject_non_fqdn_helo_hostname,
        reject_unknown_helo_hostname

smtpd_client_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_non_fqdn_helo_hostname,
        permit

# SPF
policyd-spf_time_limit = 3600

# OpenDKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:var/run/opendkim/opendkim.sock
non_smtpd_milters = unix:var/run/opendkim/opendkim.sock

# IP address per domain
sender_dependent_default_transport_maps = pcre:/etc/postfix/sdd_transport.pcre

Let's look at the details of this config.

smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key

According to the Khabrovites, this block contains `disinformation and incorrect theses`.Only 8 years after the start of my career, I began to understand how SSL works.

Therefore, I will take the liberty of describing how to use SSL (without answering the questions "How does it work?" and "Why does it work?").

The basis of modern encryption is the creation of a key pair (two very long strings of characters).

One "key" is private, the other key is "public". The private key is kept very carefully secret. We distribute the public key to everyone.

Using the public key, you can encrypt a string of text so that only the owner of the private key can decrypt it.
Well, that's the whole basis of technology.

Step #1 - https sites.
The browser, when accessing the site, learns from the web server that the site is https and therefore requests the public key.
The web server gives the public key. The browser uses the public key to encrypt the http-request and sends it.
The content of the http-request can only be read by the one who has the private key, i.e. only the server that is being accessed.
Http-request contains at least a URI. Therefore, if a country tries to restrict access not to the entire site, but to a specific page, then this cannot be done for https sites.

Step #2 - Encrypted response.
The web server gives a response that can be easily read along the way.
The solution is extremely simple - the browser locally generates the same private-public key pair for each https site.
And along with the request for the site's public key, it sends its local public key.
The web server remembers it and, when sending http-response, encrypts it with this public key of a specific client.
Now http-response can only be decrypted by the owner of the client's browser's private key (that is, the client itself).

Step number 3 - establishing a secure connection over a public channel.
There is a vulnerability in example No. 2 - nothing prevents well-wishers from intercepting the http-request and editing the information about the public key.
Thus, the intermediary will be perfectly able to see the entire content of messages sent and received until the communication channel is changed.
Dealing with this is extremely simple - just send the browser's public key as a message encrypted with the web server's public key.
The web server then first of all sends a response like “your public key is like this” and encrypts this message with the same public key.
The browser looks at the answer - if the message “your public key is like this” is received, then this is a 100% guarantee that this communication channel is safe.
How safe is it?
The very creation of such a secure communication channel occurs at a speed of ping * 2. For example 20ms.
The attacker must either have the private key of one of the parties in advance. Or pick up a private key in a couple of milliseconds.
Breaking one modern private key would take decades on a supercomputer.

Step 4 - public database of public keys.
Clearly, throughout this story, there is a possibility for an attacker to sit on the communication channel between the client and the server.
The ability for the client to appear as the server, and for the server to appear as the client. And emulate a pair of keys in both directions.
Then the attacker will see all the traffic and will be able to "edit" the traffic.
For example, change the address where to send money or copy the password from the online bank or block "objectionable" content.
To combat such intruders, we came up with a public database with public keys for each https site.
Each browser "knows" about the existence of about 200 such databases. It comes preinstalled with every browser.
"Knowledge" is backed by a public key from each certificate. That is, the connection with each specific certification authority cannot be faked.

Now there is a simple understanding of how to use SSL for https.
If you move your brains, it will become clear how special services can hack something in this structure. But it will cost them tremendous effort.
And for organizations smaller than the NSA or the CIA, it is almost impossible to break the existing level of protection even for vip.

I will also add about ssh connections. There are no public keys, how to be. The issue is resolved in two ways.
ssh-by-password option:
At the first connection, the ssh client should warn that we have a new public key from the ssh server here.
And with further connections, if a warning “a new public key from the ssh server” appears, it will mean that they are trying to listen to you.
Or at the first connection you were listened to, and now you communicate with the server without intermediaries.
Actually, due to the fact that the fact of wiretapping is easily, quickly and effortlessly revealed, this attack is used only in special cases for a specific client.

ssh-by-key option:
We take a flash drive, write a private key for the ssh server to it (there are terms and a lot of essential nuances for this, but I am writing an educational program, not instructions for use).
We leave the public key on the machine where the ssh client will be and we also keep it secret.
We bring the flash drive to the server, paste it, copy the private key, and burn the flash drive and scatter the ashes in the wind (or at least format it with zero padding).
That's all - after such an operation it will be impossible to hack such an ssh connection. Of course, in 10 years it will be possible to see traffic on a supercomputer - but that's a different story.

I apologize for the offtopic.

So, now that the theory is known. I'll tell you about the flow of creating an ssl certificate.

With the help of "openssl genrsa" we create a private key and "blanks" for the public key.
We send “blanks” to a third-party company, to which we pay about $ 9 for the simplest certificate.

After a couple of hours, we receive from this third-party company our “public” key and a set of several public keys.

Why would a third-party company pay for the registration of my public key is a separate issue, we will not consider it here.

Now it is clear what the meaning of the inscription:

smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key

The "/etc/ssl" folder contains all the files for ssl questions.
domain1.com is the name of the domain.
2018 is the year the keys were created.
"key" is the designation that the file is a private key.

And the meaning of this file:

smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
domain1.com is the name of the domain.
2018 is the year the keys were created.
chained - designation that there is a chain of public keys (the first is our public key and the rest are what came from the company that issued the public key).
crt - designation that there is a ready-made certificate (public key with technical explanations).

smtp_bind_address = XX.XX.XX.X0
smtp_bind_address6 = XXXX:XXXX:XXXX:XXXX:1:1:1:1

This setting is not used in this case, but is written as an example.

Because an error in this parameter will result in spam being sent from your server (without your will).

Then prove to everyone that you are not guilty.

recipient_delimiter = +

Perhaps many do not know, so this is the standard character for ranking pasem, and it is supported by most modern mail servers.

For example, if you have a mailbox "[email protected]» try to send to «[email protected]- See what happens.

inet_protocols = ipv4

Perhaps this will be confusing.

But it's not just that. Each new domain - by default only IPv4, then I turn on IPv6 for each separately.

virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

Here we specify that all incoming mail goes to dovecot.
And the rules for domain, mailbox, alias - look in the database.

/etc/postfix/mysql-virtual-mailbox-domains.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT 1 FROM virtual_domains WHERE name='%s'

/etc/postfix/mysql-virtual-mailbox-maps.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT 1 FROM virtual_users WHERE email='%s'

/etc/postfix/mysql-virtual-alias-maps.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT destination FROM virtual_aliases WHERE source='%s'

# SMTP-Auth settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

Now postfix knows that it is possible to accept mail for further sending only by authorization with dovecot.

I don't really understand why this is duplicated here. We have already indicated everything that is needed in “virtual_transport”.

But the postfix system is very old - probably it's a cast from the old days.

smtpd_recipient_restrictions =
        ...

smtpd_helo_restrictions =
        ...

smtpd_client_restrictions =
        ...

This is configured for each mail server in its own way.

I have 3 mail servers at my disposal and these settings are very different due to different usage requirements.

You need to configure it carefully - otherwise spam will flood to you or even worse - spam will flood from you.

# SPF
policyd-spf_time_limit = 3600

Setting for some plugin related to checking the SPF of incoming emails.

# OpenDKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:var/run/opendkim/opendkim.sock
non_smtpd_milters = unix:var/run/opendkim/opendkim.sock

Setting that all outgoing letters we must provide a DKIM signature.

# IP address per domain
sender_dependent_default_transport_maps = pcre:/etc/postfix/sdd_transport.pcre

This is a key detail in email routing when sending emails from php scripts.

File "/etc/postfix/sdd_transport.pcre":

/^[email protected]$/ domain1:
/^[email protected]$/ domain2:
/^[email protected]$/ domain3:
/@domain1.com$/             domain1:
/@domain2.com$/             domain2:
/@domain3.com$/             domain3:

On the left are regular expressions. On the right is a label that marks the letter.
Postfix according to the label - will take into account a few more lines of configuration for a particular letter.

How exactly postfix will be reconfigured for a particular letter will be indicated in "master.cf".

Lines 4, 5, 6 are the main ones. On behalf of which domain we send a letter - we put such a label.
But not always in php scripts in the old code the "from" field is indicated. Then the username comes to the rescue.

The article is already extensive - I would not want to be distracted by setting up nginx + fpm.

Briefly - we set our own linux-user owner for each site. And accordingly your fpm-pool.

Fpm-pool uses any version of php (it's great when you can use different versions of php and even different php.ini on the same server without problems for neighboring sites).

So, a specific linux-user "www-domain2" has a site domain2.com. This site has a code for sending emails without specifying the from field.

So even in this case, the letters will leave correctly and will never fall into spam.

My "/etc/postfix/master.cf" looks like this:

...
smtp      inet  n       -       y       -       -       smtpd
  -o content_filter=spamassassin
...
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
...
policyd-spf  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/bin/policyd-spf

spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}
...
domain1  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X1
   -o smtp_helo_name=domain1.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:1:1
   -o syslog_name=postfix-domain1

domain2  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X5
   -o smtp_helo_name=domain2.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:2:1:1
   -o syslog_name=postfix-domain2

domain3  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X2
   -o smtp_helo_name=domain3
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:5:1
   -o syslog_name=postfix-domain3

The file is not shown in full - it is already very large.
I only marked what was changed.

smtp      inet  n       -       y       -       -       smtpd
  -o content_filter=spamassassin
...
spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}

These are settings related to spamassasin, more on that later.

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

We allow you to connect to the mail server through port 587.
To do this, you must be logged in.

policyd-spf  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/bin/policyd-spf

Turn on SPF check.

apt-get install postfix-policyd-spf-python

Install the package for SPF checks above.

domain1  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X1
   -o smtp_helo_name=domain1.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:1:1
   -o syslog_name=postfix-domain1

And this is the most interesting. This is the ability to send emails for a specific domain from a specific IPv4/IPv6 address.

This is done for the sake of rDNS. rDNS is getting some string by IP address.
And for mail, this feature is used to confirm that helo exactly matches the rDNS of the address from which the email was sent.

If helo does not match the mail domain on behalf of whom the letter was sent, spam points are awarded.

Helo does not comply with rDNS - a lot of spam points are awarded.
Accordingly, each domain must have its own IP address.
For OVH, it is possible to specify rDNS in the console.
For tech.ru, the issue is resolved through support.
For AWS, the issue is resolved through support.
"inet_protocols" and "smtp_bind_address6" - we enable IPv6 support.
For IPv6, you also need to register rDNS.
"syslog_name" - and this is for the convenience of reading the logs.

Buy certificates recommend here.

Setting up a postfix + dovecot link here.

SPF setting.

============= Dovecot ==============

apt-get install dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql dovecot-antispam

Setting up mysql, install the packages themselves.

File "/etc/dovecot/conf.d/10-auth.conf"

disable_plaintext_auth = yes
auth_mechanisms = plain login

Authorization is encrypted only.

File "/etc/dovecot/conf.d/10-mail.conf"

mail_location = maildir:/var/mail/vhosts/%d/%n

Here we indicate the location of the storage of letters.

I want them to be stored in files and grouped by domain.

File "/etc/dovecot/conf.d/10-master.conf"

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    address = XX.XX.XX.X1, XX.XX.XX.X2, XX.XX.XX.X5, [XXXX:XXXX:XXXX:XXXX:1:1:1:1], [XXXX:XXXX:XXXX:XXXX:1:2:1:1], [XXXX:XXXX:XXXX:XXXX:1:1:5:1]
    port = 993
    ssl = yes
  }
}
service pop3-login {
  inet_listener pop3 {
    port = 0
  }
  inet_listener pop3s {
    address = XX.XX.XX.X1, XX.XX.XX.X2, XX.XX.XX.X5, [XXXX:XXXX:XXXX:XXXX:1:1:1:1], [XXXX:XXXX:XXXX:XXXX:1:2:1:1], [XXXX:XXXX:XXXX:XXXX:1:1:5:1]
    port = 995
    ssl = yes
  }
}
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}
service imap {
}
service pop3 {
}
service auth {
  unix_listener auth-userdb {
    mode = 0600
    user = vmail
  }

  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
  user = dovecot
}
service auth-worker {
  user = vmail
}
service dict {
  unix_listener dict {
  }
}

This is the main dovecot configuration file.
Here we disable insecure connections.
And enable secure connections.

File "/etc/dovecot/conf.d/10-ssl.conf"

ssl = required
ssl_cert = </etc/nginx/ssl/domain1.com.2018.chained.crt
ssl_key = </etc/nginx/ssl/domain1.com.2018.key
local XX.XX.XX.X5 {
  ssl_cert = </etc/nginx/ssl/domain2.com.2018.chained.crt
  ssl_key =  </etc/nginx/ssl/domain2.com.2018.key
}

Set up ssl. We indicate that ssl is required.
And the certificate itself. And an important detail is the "local" directive. Specifies which ssl certificate to use when connecting to which local IPv4.

By the way, IPv6 is not configured here, I will correct this omission as a thread later.
XX.XX.XX.X5 (domain2) - no certificate. To connect clients, you need to specify domain1.com.
XX.XX.XX.X2 (domain3) - there is a certificate, you can specify domain1.com or domain3.com to connect clients.

File "/etc/dovecot/conf.d/15-lda.conf"

protocol lda {
  mail_plugins = $mail_plugins sieve
}

This will be needed later for spamassassin.

File "/etc/dovecot/conf.d/20-imap.conf"

protocol imap {
  mail_plugins = $mail_plugins antispam
}

This is an antispam plugin. Needed for training spamassasin at the time of transfer to / from the "Spam" folder.

File "/etc/dovecot/conf.d/20-pop3.conf"

protocol pop3 {
}

There is just such a file.

File "/etc/dovecot/conf.d/20-lmtp.conf"

protocol lmtp {
  mail_plugins = $mail_plugins sieve
  postmaster_address = [email protected]
}

Setting lmtp.

File "/etc/dovecot/conf.d/90-antispam.conf"

plugin {
  antispam_backend = pipe
  antispam_trash = Trash;trash
  antispam_spam = Junk;Spam;SPAM
  antispam_pipe_program_spam_arg = --spam
  antispam_pipe_program_notspam_arg = --ham
  antispam_pipe_program = /usr/bin/sa-learn
  antispam_pipe_program_args = --username=%Lu
}

Spamassasin training settings at the time of transfer to / from the "Spam" folder.

File "/etc/dovecot/conf.d/90-sieve.conf"

plugin {
  sieve = ~/.dovecot.sieve
  sieve_dir = ~/sieve
  sieve_after = /var/lib/dovecot/sieve/default.sieve
}

A file that specifies what to do with incoming messages.

File "/var/lib/dovecot/sieve/default.sieve"

require ["fileinto", "mailbox"];

if header :contains "X-Spam-Flag" "YES" {
        fileinto :create "Spam";
}

It is necessary to compile the file: "sievec default.sieve".

File "/etc/dovecot/conf.d/auth-sql.conf.ext"

passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}

Specifying sql files for authorization.
And the file itself - as a way of authorization.

File "/etc/dovecot/dovecot-sql.conf.ext"

driver = mysql
connect = host=127.0.0.1 dbname=servermail user=usermail password=password
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

This corresponds to similar settings for postfix.

File "/etc/dovecot/dovecot.conf"

protocols = imap lmtp pop3
listen = *, ::
dict {
}
!include conf.d/*.conf
!include_try local.conf

Main configuration file.
What is important is that we specify-add protocols here.

============= SpamAssassin ==============

apt-get install spamassassin spamc

Let's install the packages.

adduser spamd --disabled-login

Add a user on whose behalf.

systemctl enable spamassassin.service

Enable auto-load spamassassin service on boot.

File "/etc/default/spamassassin":

CRON=1

By enabling automatic updating of rules by default.

File "/etc/spamassassin/local.cf":

report_safe 0

use_bayes          1
bayes_auto_learn   1
bayes_auto_expire  1
bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn      DBI:mysql:sa:localhost:3306
bayes_sql_username sa
bayes_sql_password password

It is necessary to make a database “sa” in mysql with the user “sa” with the password “password” (replace with something adequate).

report_safe - this will send a spam message report instead of a letter.
use_bayes are spamassassin machine learning settings.

The rest of the spamassassin settings were applied earlier in the article.

General setting "spamassassin".
About moving new Spam emails to the IMAP "Spam" folder.
About a simple bunch of Dovecot + SpamAssassin.
I recommend reading the spamassasin learning theory when moving letters in imap folders (and do not recommend it for use).

============= Community Appeal ==============

I would also like to throw an idea into the community on how to increase the level of security of forwarded letters. Since I'm so deeply immersed in the topic of mail.

So that the user could create a key pair on his client (outlook, thunderbird, browser-plugin, ...). Public and private. Public - send to DNS. Private - save on the client. Mail servers would be able to use the public key to send to a specific recipient.

And to protect against spam with such letters (yes, the mail server will not be able to view the content) - you will need to enter 3 rules:

  1. Mandatory true DKIM signature, mandatory SPF, mandatory rDNS.
  2. A neural network on the topic of learning anti-spam + a database for it on the client side.
  3. The encryption algorithm must be such that the sending side must spend 100 times more CPU power on encryption than the receiving side.

In addition to public letters, develop a standard for a letter of offer to “start a secure correspondence”. One of the users (mailbox) sends a letter with an attachment to another mailbox. In the letter, a text-sentence to start a secure communication channel for correspondence and the public key of the owner of the mailbox (with the private key on the client side).

You can even make a couple of keys specifically for each correspondence. The recipient user can accept this offer and send their public key (also made specifically for this correspondence). Next, the first user sends a service control letter (encrypted with the second user's public key), upon receipt of which the second user can consider the formed communication channel reliable. Then the second user sends a control letter - and then the first user can also consider the formed channel secure.

To combat the interception of keys on the road, it is necessary to provide for the possibility of transferring at least one public key using a flash drive in the protocol.

And the most important thing is that it all works (the question is “who will pay for this?”):
Introduce postal certificates worth from $10 for 3 years. Which will allow the sender to specify in dns that "my public keys are over there." And they will give the opportunity to start a secure connection. At the same time - to accept such connections for free.
gmail is finally monetizing its users. For $ 10 in 3 years - the right to create secure channels of correspondence.

============= Conclusion ==============

To test the entire article, I was going to rent a dedicated server for a month and buy a domain with an ssl certificate.

But life circumstances have developed so this issue dragged on for 2 months.
And when free time appeared again, I decided to publish the article as it is, and not risk that the publication would drag on for another year.

If there are a lot of questions like “but it’s not described in sufficient detail here”, then there will probably be the strength to still take a dedicated server with a new domain and a new SSL certificate and describe it in more detail and, most importantly, identify all the missed important details.

I would also like to receive feedback on the idea of ​​mail certificates. If you like the idea, I will try to find the strength to write a draft for rfc.

When copying large pieces of an article, indicate a link to this article.
When translating into any other language, provide a link to this article.
I will try to translate into English myself and leave cross-references.


Source: habr.com

Add a comment