diff --git a/homepage/views/articles/setting_up_a_mail_server.pug b/homepage/views/articles/setting_up_a_mail_server.pug index 792a69b..39af64a 100644 --- a/homepage/views/articles/setting_up_a_mail_server.pug +++ b/homepage/views/articles/setting_up_a_mail_server.pug @@ -37,29 +37,19 @@ block article li #[a(href='#art-9') Setting up a ClamAV antivirus] h3#art-1 #[a(href='#art-1') 1. Introduction] - p I use Postfix as a SMTP and Dovecot (with Pigeonhole (Sieve)) as an IMAP server. ClamAV for an antivirus. For anti-spam I use SpamAssassin. For DKIM and DMARC — OpenDKIM and OpenDMARC respectively. I could use rspamd instead of the latter three, but it doesn't work on Raspberry Pi. + p I use Postfix as a SMTP and Dovecot (with Pigeonhole (Sieve)) as an IMAP server. ClamAV for an antivirus. For antispam I use SpamAssassin. For DKIM and DMARC — OpenDKIM and OpenDMARC respectively. p It is vital to make the DKIM, DMARC and SPF DNS records. Also, if you want your mail server to be trusted by every other mail servers then you should get a static IP-address if you don't yet. And you have to ask your ISP to edit PTR DNS record for your static IP-address to point to your domain. - p Unfortunately for me I don't have neither, and I'm afraid that even if I get the static IP-address, my ISP won't edit PTR record, because that's available only for bussiness customers. + p Unfortunately for me I don't have neither, and I'm afraid that even if I get the static IP-address, my ISP won't edit PTR record, because that's available only for bussiness customers. And about domains, there is such things as untrustworthy abused TLDs, and top TLD is in that list. That's definitely because it's a cheap TLD. p Server is configured in a simple way using PAM (real system users) with user's passwords and with mail stored in ~/Maildir. h3#art-2 #[a(href='#art-2') 2. Installing] - p You need to install following packages: #[code postfix], #[code dovecot], #[code pidgeonhole] (or could be #[code dovecot-sieve]), #[code clamav], #[code opendkim], #[code opendmarc] and #[code spamassassin]. + p You need to install following packages: #[code postfix], #[code dovecot], #[code pidgeonhole] (or could be #[code dovecot-sieve]), #[code clamav], #[code opendkim], #[code opendmarc], and #[code spamassassin]. h3#art-3 #[a(href='#art-3') 3. Postfix SMTP server] - p Its configuration files are in directory #[code /etc/postfix]. First we need to work with #[code main.cf] file. Then configure services in #[code master.cf]. Also I'll show you how to make aliases for users. + p Its configuration files are in directory #[code /etc/postfix]. There are two configuration files we'll work with. The first one is a #[code main.cf] file. Then we configure services in #[code master.cf]. Also I'll show you how to make aliases for users. h4#art-3-1 #[a(href='#art-3-1') 3.1. main.cf] - p Set #[code myhostname] to a hostname of a server (e.g. #[code mail.example.org]). Set #[code mydomain] to your domain name (e.g. #[code example.org]). Set #[code myorigin] to #[code $mydomain] to set origin of mail being sent from your server. - p #[code mydestination] is a list of domains that are delivered through a local transport. If server should go outside then this parameter must include #[code $mydomain] alongside names for the local machine. E.g. #[code $myhostname, localhost, $mydomain, mail.$mydomain]. - p #[code local_recipient_maps] are lookup tables with all names and/or addresses of local recipients. In my case it set to #[code unix:passwd.byname $alias_maps]. - p I have #[code inet_interfaces = all] to listen on all the interfaces. - p In #[code mynetworks], as stated in a Postfix's manual, we specify a list of “trusted” clients that have more privileges than “strangers”. In particular, such clients are allowed to relay mail through Postfix. I have it set to localhost and my LAN. - p In #[code alias_maps] we specify a list of lookup tables that contain aliases for existing users. And in #[code alias_database] just add #[code $alias_maps]. #[code alias_database] is, as stated in a manual, separate because not all the tables specified with #[code $alias_maps] have to be local files. - p #[code recipient_delimeter = +]. Here we set a delimeter to a plus sign (that's just a usual practice that I obeyed). - p I use a Maildir-style mailboxes, so #[code home_mailbox] is set to #[code Maildir/] (slash is necessary). - p We use Dovecot, so #[code mailbox_transport] should be set to #[code lmtp:unix:private/dovecot-lmtp]. Here we point to where Dovecot LMTP server listens, in our case it is a UNIX-socket. - p Optionaly, you can set #[code inet_protocols] to IP versions used by you, I set it just to #[code ipv4] for a quite legitimate reason of not having IPv6 address. :) - p Here are all the modified parameters listed: + p Let's take a look at all base options that should be modified: pre | myhostname = mail.example.org | mydomain = example.org @@ -84,10 +74,118 @@ block article | mailbox_transport = lmtp:unix:private/dovecot-lmtp | | inet_protocols = ipv4 + p Now let's clarify what are they doing. + p Set #[code myhostname] to a hostname of a server (e.g. #[code mail.example.org]). Set #[code mydomain] to your domain name (e.g. #[code example.org]). Set #[code myorigin] to #[code $mydomain] to set origin of mail being sent from your server. + p #[code mydestination] is a list of domains that are delivered through a local transport. If server should go outside then this option must include #[code $mydomain] alongside names for the local machine. E.g. #[code $myhostname, localhost, $mydomain, mail.$mydomain]. + p #[code local_recipient_maps] are lookup tables with all names and/or addresses of local recipients. In my case it set to #[code unix:passwd.byname $alias_maps]. + p I have #[code inet_interfaces = all] to listen on all the interfaces. + p In #[code mynetworks], as stated in a Postfix's manual, we specify a list of “trusted” clients that have more privileges than “strangers”. In particular, such clients are allowed to relay mail through Postfix. I have it set to localhost and my LAN. + p In #[code alias_maps] we specify a list of lookup tables that contain aliases for existing users. And in #[code alias_database] just add #[code $alias_maps]. #[code alias_database] is, as stated in a manual, separate because not all the tables specified with #[code $alias_maps] have to be local files. + p #[code recipient_delimeter = +]. Here we set a delimeter to a plus sign (that's just a usual practice that I obeyed). + p I use a Maildir-style mailboxes, so #[code home_mailbox] is set to #[code Maildir/] (slash is necessary). + p We use Dovecot, so #[code mailbox_transport] should be set to #[code lmtp:unix:private/dovecot-lmtp]. Here we point to where Dovecot LMTP server listens, in our case it is a UNIX-socket. + p Optionaly, you can set #[code inet_protocols] to IP versions used by you, I set it just to #[code ipv4] for a quite legitimate reason of not having IPv6 address. :) It is a space-separated list, so to support both write #[code ipv4 ipv6]. + p Next I'll cover how to make encryption working, set up milters (mail filters (i.e. OpenDKIM and OpenDMARC)), and restrictions. + p Next let's configure our milters: + pre + | milter_default_action = accept + | milter_protocol = 6 + | smtpd_milters = + | unix:/var/spool/opendmarc/opendmarc.sock + | unix:/var/spool/opendkim/opendkim.sock + | inet 192.168.0.54:7357 + | non_smtpd_milters = $smtpd_milters + p #[code milter_default_action] specifies default action of a milter. Here we accept an e-mail message. + p #[code milter_protocol] specifies protocol version used by milters, current is 6. + p #[code smtpd_milters] is a list of milters the messages will go through. You can connect to milter with internet protocol with #[code inet:], with a unix socket #[code unix:]. The last one with port 7357 is a ClamAV by the way. + p Now lets do some tweaks: + pre + | biff = no + | strict_rfc821_envelopes = yes + | disable_vrfy_command = yes + | smtpd_helo_required = yes + | smtpd_delay_reject = yes + | + | mailbox_size_limit = 0 + | message_size_limit = 52428800 + p #[code biff] set to no, so the local service for new mail notifications disabled. We run on a server machine, we don't need them. + p #[code strict_rfc821_envelopes] set to yes require addresses to be enclosed with <>. + p Disabling VRFY command with #[code disable_vrfy_command] set to yes prevents some email addresses harvesting techniques. + p #[code smtpd_helo_required] requires remote client to send HELO or EHLO command. This may stop some poorly written spam bots. + p #[code smtpd_delay_reject] makes Postfix wait until RCPT TO command before evaluating some restrictions. + p #[code mailbox_size_limit] and #[code message_size_limit] sets maximum size of a whole mailbox and of each email. Here I set no limit for a mailbox, and max of 50MiB for an email message. + p And now it's time for SASL configuration: + pre + | smtpd_sasl_type = dovecot + | smtps_sasl_path = private/auth + | smtps_sasl_auth_enable = yes + | smtpd_sasl_security_options = noanonymous + | smtpd_sasl_local_domain = $mydomain + | broken_sasl_auth_clients = no + p It's pretty clear. We chose dovecot as our SASL, set path to it withing /var/spool, and enable it. Then we set option to not allow anonymous connections. Set our domain for SASL, and wether let the clients with obsolete version of AUTH command in or not, we chose not to. + p It's time for encryption, lets start with client part of it. + pre + | smtp_use_tls = yes + | smtp_tls_security_level = may + | smtp_tls_note_starttls_offer = yes + | smtp_tls_connection_reuse = yes + | smtp_tls_key_file = /etc/letsencrypt/live/example.org/privkey.pem + | smtp_tls_cert_file = /etc/letsencrypt/live/example.org/fullchain.pem + | smtp_tls_mandatory_protocols = !SSLv2,!SSLv3,!TLSv1,!TLSv1.1 + | smtp_tls_protocols = $smtp_tls_mandatory_protocols + | smtp_tls_mandatory_ciphers = high + | smtp_tls_exclude_ciphers = aNULL, MD5, CAMELLIA + p First we enable it with a #[code smtp_use_tls] option. Set security level (#[code smtp_tls_security_level]) to may, since not every SMTP server out there have encryption. Noting servers of our ability to use encryption with option #[code smtp_tls_note_starttls_offer]. Set to reuse connection instead of opening the new one each time. + p #[code smtp_tls_key_file] and #[code smtp_tls_cert_file] are paths to our encryption key and certificate. + p #[code smtp_tls_mandatory_protocols] and #[code smtp_tls_protocols] here we disallow old vulnerable protocols to use. Here only TLS version 1.2 and 1.3 are allowed. + p In #[code smtp_tls_mandatory_ciphers] we declare to use only good secure cyphers. And in #[code smtp_tls_exclude_ciphers] we exclude the most weak one. + p With server part everything is the same, just a few more options added: + pre + | smtpd_use_tls = yes + | smtpd_tls_security_level = may + | smtpd_tls_auth_only = yes + | smtpd_tls_key_file = /etc/letsencrypt/live/aravs.ru/privkey.pem + | smtpd_tls_cert_file = /etc/letsencrypt/live/aravs.ru/fullchain.pem + | smtpd_tls_mandatory_protocols = $smtp_tls_mandatory_protocols + | smtpd_tls_protocols = $smtpd_tls_mandatory_protocols + | smtpd_tls_mandatory_ciphers = $smtp_tls_mandatory_ciphers + | smtpd_tls_exclude_ciphers = $smtp_tls_exclude_ciphers + | smtpd_tls_loglevel = 1 + | smtpd_tls_received_header = yes + | smtpd_tls_session_cache_timeout = 3600s + | tls_random_source = dev:/dev/urandom + p Security level is may, but for auth TLS is required. #[code smtpd_tls_loglevel] is for logging a summary of a TLS handshake. + p #[code smtpd_tls_received_header] makes Postfix include information about the protocol and cypher used to a Received: header. + p #[code smtpd_tls_session_cache_timeout] is for how long to store session. #[code tls_random_source] is for setting an entropy source. + p The final part is my "favorite". :) The restrictions! There is a set of them for each stage the message falls through. Here's the ones I configured:#[code smtpd_helo_restrictions], #[code smtpd_relay_restrictions], #[code smtpd_data_restrictions], #[code smtpd_sender_restrictions], and #[code smtpd_recipient_restrictions]. + p So lets roll. + pre + | smtpd_helo_restrictions = + | reject_unknown_helo_hostname, + | reject_invalid_helo_hostname, + | reject_non_fqdn_helo_hostname + pre + | smtpd_relay_restrictions = + | permit_mynetworks, + | permit_sasl_authenticated, + | reject_unauth_destination + pre + | smtpd_data_restrictions = + | reject_multi_recipient_bounce, + | reject_unauth_pipelining + pre + | smtpd_sender_restrictions = + | permit_sasl_authenticated, + | reject_non_fqdn_sender, + | reject_unknown_sender_domain + pre + | smtpd_recipient_restrictions = + | reject_unknown_recipient_domain, + | reject_non_fqdn_recipient h4#art-3-2 #[a(href='#art-3-2') 3.2. master.cf] - p Here are all needed lines to be added or modified: + p The following services are needed: #[code smtp], #[code submission], #[code smtps], and we add #[code spamassassin] service. The rest in this file is untouchable. pre | smtp inet n - n - - smtpd | -o content_filter=spamassassin @@ -96,6 +194,8 @@ block article | -o smtpd_tls_security_level=encrypt | -o smtpd_sasl_auth_enable=yes | -o smtpd_tls_auth_only=yes + | -o milter_macro_daemon_name=ORIGINATING + | -o content_filter=spamassassin | smtps inet n - n - - smtpd | -o content_filter=spamassassin | -o syslog_name=postfix/smtps @@ -105,6 +205,7 @@ block article | spamassassin unix - n n - - pipe | user=spamd argv=/bin/vendor_perl/spamc | -e /sbin/sendmail -oi -f ${sender} ${recipient} + p #[code smtp] is listening on port 25, and #[code smtps] on port 465, and are used for cross-server talking. #[code submission] is listening on port 587 and is used by mail client to send mail. h4#art-3-3 #[a(href='#art-3-3') 3.3. User aliases] p User aliases are in #[code aliases] file. They has a form "#[code <alias>: <username>]", e.g. #[code me: arav]. Where #[code username] may be other alias. After modifications you need to run #[code newaliases] program to update #[code aliases.db] database file. @@ -200,7 +301,7 @@ block article h3#art-8 #[a(href='#art-8') 8. DNS records] h4#art-8-1 #[a(href='#art-8-1') 8.1. MX and A/AAAA] - p It's good to have a dedicated A (IPv4 address) or AAAA (IPv6 address) record for a mail server's hostname instead of a CNAME record so other servers won't need to do two DNS requests. Hostname is usually mail.example.org if there's just one server, you can call it whatever you want. Remind you that we set it in Postfix in #[code myhostname] parameter. + p It's good to have a dedicated A (IPv4 address) or AAAA (IPv6 address) record for a mail server's hostname instead of a CNAME record so other servers won't need to do two DNS requests. Hostname is usually mail.example.org if there's just one server, you can call it whatever you want. Remind you that we set it in Postfix in #[code myhostname] option. p And A record looks like this: pre | mail  IN  86400  A  203.0.113.4