diff options
Diffstat (limited to 'articles/mail-server.html')
| -rw-r--r-- | articles/mail-server.html | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/articles/mail-server.html b/articles/mail-server.html new file mode 100644 index 0000000..12a3264 --- /dev/null +++ b/articles/mail-server.html | |||
| @@ -0,0 +1,723 @@ | |||
| 1 | <!DOCTYPE html> | ||
| 2 | <html lang=en> | ||
| 3 | <head> | ||
| 4 | <title></title> | ||
| 5 | <meta charset="utf-8"/> | ||
| 6 | <link rel="shortcut icon" href="favicon.ico"/> | ||
| 7 | <link rel='stylesheet' href='../style.css'/> | ||
| 8 | <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| 9 | </head> | ||
| 10 | <body> | ||
| 11 | <header><h1>Postfix and Dovecot Mail Server</h1></header> | ||
| 12 | <main> | ||
| 13 | |||
| 14 | <p>Postfix and dovecot will be the two primary pieces of our mail sever. | ||
| 15 | Postfix is the mail transport agent that handles the sending and | ||
| 16 | receiving of mail and dovecot is the IMAP server that will allow us to | ||
| 17 | access our mail from a mail client such as mutt. The server will also | ||
| 18 | have several other supporting components, a complete list of which is:</p> | ||
| 19 | |||
| 20 | <ul> | ||
| 21 | <li><em>SpamAssassin</em> for spam filtering</li> | ||
| 22 | <li><em>OpenDKIM</em> for DKIM verification and signing</li> | ||
| 23 | <li><em>Postgrey</em> for greylisting</li> | ||
| 24 | <li><em>Policyd-SPF</em> for SPF verification</li> | ||
| 25 | <li><em>OpenDMARC</em> for DMARC verification</li> | ||
| 26 | </ul> | ||
| 27 | |||
| 28 | <p>You can use <a href=https://git.chudnick.com/mail-tools/tree/server/deploy-server> | ||
| 29 | this script</a> I have written to automate this process, but I would | ||
| 30 | recommend that you run through the tutorial first to understand | ||
| 31 | what is being done.</p> | ||
| 32 | |||
| 33 | <p>Please note that this tutorial is loosely intended for small personal mail | ||
| 34 | servers. Using PAM for authentication, as is done here, is not a scalable solution | ||
| 35 | for working with a large number of users. I do plan on covering Dovecot LDAP | ||
| 36 | authentication at some point which would be a better solution in an enterprise | ||
| 37 | setting.</p> | ||
| 38 | |||
| 39 | <h2>Install Packages</h2> | ||
| 40 | <p>Let's start by installing the required packages. Note that if you already | ||
| 41 | have Apache installed on the server, replace <em>python3-certbot-nginx</em> | ||
| 42 | with <em>python3-certbot-apache</em>.</p> | ||
| 43 | <pre><code>apt install postfix dovecot-imapd dovecot-sieve opendkim opendkim-tools spamassassin gnupg postgrey postfix-policyd-spf-python opendmarc dbconfig-no-thanks certbot python3-certbot-nginx</code></pre> | ||
| 44 | <p>During the installation of Postfix you will get a Debconf prompt in which | ||
| 45 | you need to select "Internet Site" and then provide your domain name, | ||
| 46 | <strong>example.com</strong>.</p> | ||
| 47 | |||
| 48 | <h2>Get a certificate</h2> | ||
| 49 | <p>Now we'll use Certbot to get a certificate for our server. If you are | ||
| 50 | using Apache replace <em>nginx</em> with <em>apache2</em>.</p> | ||
| 51 | |||
| 52 | <pre><code>systemctl stop nginx | ||
| 53 | certbot certonly --standalone -d <strong>mail.example.com</strong> | ||
| 54 | systemctl start nginx</code></pre> | ||
| 55 | |||
| 56 | <h2>Postfix Main Configuration</h2> | ||
| 57 | <p>In this section we will be doing the bulk of the postfix configuration. | ||
| 58 | The postconf command used throughout appends (or changes) | ||
| 59 | the specified configuration item in /etc/postfix/main.cf</p> | ||
| 60 | |||
| 61 | <h3>Network Configuration</h3> | ||
| 62 | |||
| 63 | <p>Let's start by configuring some network and domain information.</p> | ||
| 64 | |||
| 65 | <pre><code>postconf -e "myorigin = <strong>example.com</strong>" | ||
| 66 | postconf -e "mydestination = \$myhostname, \$mydomain, localhost" | ||
| 67 | postconf -e "mynetworks = 127.0.0.0/8 [::1]/128" | ||
| 68 | postconf -e "myhostname = <strong>mail.example.com</strong>" | ||
| 69 | </code></pre> | ||
| 70 | |||
| 71 | <p>Next, point postfix to the cerbot key and certificate, as well as the distro's | ||
| 72 | CA certificates.</p> | ||
| 73 | |||
| 74 | <pre><code>postconf -e "smtpd_tls_key_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem" | ||
| 75 | postconf -e "smtpd_tls_cert_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem" | ||
| 76 | postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt" | ||
| 77 | </code></pre> | ||
| 78 | |||
| 79 | <p>Harden the TLS configuration by forcing strong | ||
| 80 | protocols and ciphers, and requiring that authentication occur only over an | ||
| 81 | encrypted session.</p> | ||
| 82 | <pre><code><em># Require authentication over TLS and optionally use it for sending and receiving mail</em> | ||
| 83 | postconf -e "smtpd_tls_auth_only = yes" | ||
| 84 | postconf -e "smtpd_tls_security_level = may" | ||
| 85 | postconf -e "smtp_tls_security_level = may" | ||
| 86 | |||
| 87 | <em># Force the use of TLSv1.2 or TLSv1.3</em> | ||
| 88 | postconf -e 'smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1' | ||
| 89 | postconf -e 'smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1' | ||
| 90 | postconf -e 'smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1' | ||
| 91 | postconf -e 'smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1' | ||
| 92 | |||
| 93 | <em># Prefer server ciphers</em> | ||
| 94 | postconf -e "tls_preempt_cipherlist = yes" | ||
| 95 | |||
| 96 | <em># Force strong ciphers</em> | ||
| 97 | postconf -e "smtpd_tls_ciphers = high" | ||
| 98 | postconf -e "smtpd_tls_mandatory_ciphers = high" | ||
| 99 | postconf -e "smtp_tls_ciphers = high" | ||
| 100 | postconf -e "smtp_tls_mandatory_ciphers = high" | ||
| 101 | postconf -e "smtpd_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH" | ||
| 102 | postconf -e "smtp_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH" | ||
| 103 | </code></pre> | ||
| 104 | |||
| 105 | <h3>Local Recipients and Aliases</h3> | ||
| 106 | |||
| 107 | <p>Here we configure the bulk of the postfix built-in security settings which are | ||
| 108 | structured as a series of access restrictions. Do not edit these settings without | ||
| 109 | first reading the Postfix documentation as an incorrect change could inadvertently | ||
| 110 | make your server an open relay.</p> | ||
| 111 | |||
| 112 | <pre><code>postconf -e "smtpd_helo_required = yes" | ||
| 113 | postconf -e "smtpd_sender_login_maps = proxy:hash:/etc/postfix/login_maps" | ||
| 114 | postconf -e "smtpd_helo_restrictions = reject_unknown_helo_hostname, reject_non_fqdn_helo_hostname" | ||
| 115 | postconf -e "smtpd_sender_restrictions = reject_sender_login_mismatch, reject_non_fqdn_sender, reject_unknown_sender_domain" | ||
| 116 | postconf -e "smtpd_recipient_restrictions = reject_non_fqdn_recipient, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/postgrey, check_policy_service unix:private/policyd-spf, reject_rbl_client zen.spamhaus.org" | ||
| 117 | postconf -e "smtpd_relay_restrictions = permit_sasl_authenticated, reject_unauth_destination" | ||
| 118 | postconf -e "smtpd_data_restrictions = reject_unauth_pipelining" | ||
| 119 | |||
| 120 | <em># Disable VRFY command to prevent harvesting of user accounts on system</em> | ||
| 121 | postconf -e "disable_vrfy_command = yes" | ||
| 122 | |||
| 123 | <em># Change smptd banner (hide distribution)</em> | ||
| 124 | postconf -e "smtpd_banner = \$myhostname ESMTP \$mail_name"</code></pre> | ||
| 125 | |||
| 126 | <p>Now, configure the local mail recipients and some aliases. We'll create | ||
| 127 | an account called <strong>mailadmin</strong> to receive mail addressed to | ||
| 128 | several other accounts. This is to keep <em>administrative</em> mail separate, but | ||
| 129 | you can certainly alias these to your main account later if you would prefer to see | ||
| 130 | it there.</p> | ||
| 131 | |||
| 132 | <pre><code><em># Set a custom local_recipient_maps here in order to avoid accepting mail for all local accounts</em> | ||
| 133 | postconf -e "local_recipient_maps = proxy:hash:/etc/postfix/local_maps \$alias_maps" | ||
| 134 | |||
| 135 | <em># You will need to manually set a password later to login as mailadmin</em> | ||
| 136 | adduser --disabled-login --shell /usr/sbin/nologin --gecos "" mailadmin | ||
| 137 | echo "# postfix aliases | ||
| 138 | postmaster: mailadmin | ||
| 139 | root: mailadmin | ||
| 140 | dmarc: mailadmin | ||
| 141 | " > /etc/aliases | ||
| 142 | |||
| 143 | <em># Update address databases</em> | ||
| 144 | echo "mailadmin@<em>mail.example.com</em> mailadmin" > /etc/postfix/login_maps | ||
| 145 | echo "mailadmin mailadmin" > /etc/postfix/local_maps | ||
| 146 | newaliases | ||
| 147 | postmap /etc/postfix/login_maps | ||
| 148 | postmap /etc/postfix/local_maps | ||
| 149 | </code></pre> | ||
| 150 | |||
| 151 | <h3>Mail Delivery</h3> | ||
| 152 | |||
| 153 | |||
| 154 | <p>These commands configure our mail delivery preferences. Mail will be | ||
| 155 | delivered inside a user's home folder with a maildir-style mailbox using | ||
| 156 | dovecot.</p> | ||
| 157 | |||
| 158 | <pre><code><em># Maildir delivery to $HOME/Mail/Inbox/</em> | ||
| 159 | postconf -e "home_mailbox = Mail/Inbox/" | ||
| 160 | <em># Deliver mail with Dovecot</em> | ||
| 161 | postconf -e "mailbox_command = /usr/lib/dovecot/deliver"</code></pre> | ||
| 162 | |||
| 163 | <h3>Header and Body Checks</h3> | ||
| 164 | |||
| 165 | <p>Header and body checks allow for some simple content filtering within Postfix. | ||
| 166 | This is done by scanning a message line by line for a configured regex string, | ||
| 167 | nothing more. For example, the first header check listed will reject a message | ||
| 168 | with an attachment of <em>ransomware.exe</em> but will not block it if sent with | ||
| 169 | no extension. This is mostly a protection against uneducated users and poorly | ||
| 170 | written mail clients. Other checks block vulnerabilities and improve privacy.</p> | ||
| 171 | |||
| 172 | <p>Create a new file <strong>/etc/postfix/header_checks</strong>, then open it in a | ||
| 173 | text editor and add the following</p> | ||
| 174 | <pre><code><em># Block files with common executable extensions</em> | ||
| 175 | /name=[^>]*\.(exe|pif|com|dll|vbs|bat|sh|bash|so|zip|tar|gz|cpio)/ REJECT | ||
| 176 | |||
| 177 | <em># Block message/partial vulnerability</em> | ||
| 178 | /message\/partial/ REJECT | ||
| 179 | |||
| 180 | <em># Remove Received string that is created when spamassassin reinjects message into postfix</em> | ||
| 181 | <em># This is to prevent leaking the userid of the spamassassin user</em> | ||
| 182 | /^Received:.*userid.*/ IGNORE | ||
| 183 | |||
| 184 | <em># Remove User-Agent strings from headers</em> | ||
| 185 | /^User-Agent: .*/ IGNORE</code></pre> | ||
| 186 | |||
| 187 | <p>Create another new file <strong>/etc/postfix/body_checks</strong>, and add this</p> | ||
| 188 | <pre><code><em># Block messages with iframes</em> | ||
| 189 | /<iframe/ REJECT" > /etc/postfix/body_checks</code></pre> | ||
| 190 | |||
| 191 | <p>And then run these commands to point postfix to the check files.</p> | ||
| 192 | <pre><code>postconf -e "header_checks = regexp:/etc/postfix/header_checks" | ||
| 193 | postconf -e "body_checks = regexp:/etc/postfix/body_checks"</code></pre> | ||
| 194 | |||
| 195 | <h2>Postfix Master Configuration</h2> | ||
| 196 | <h3>SMTP client</h3> | ||
| 197 | <p>This simple command configures the SMTP client process that is responsible | ||
| 198 | for sending your mail to other mail servers.</p> | ||
| 199 | |||
| 200 | <pre><code>postconf -M "smtp/unix=smtp unix - - y - - smtp"</code></pre> | ||
| 201 | |||
| 202 | <h3>Postscreen and SMTP Recipient</h3> | ||
| 203 | <p>Postscreen is a kind of firewall that sits in front of the Postfix SMTPD | ||
| 204 | process and receives all incoming traffic. Postscreen will drop connections | ||
| 205 | from IPs on a DNS blacklst, or from clients that violate the SMTP protocol by | ||
| 206 | speaking out of turn or sending non-SMTP commands. This adds up to less spam | ||
| 207 | connections and therefore a much lighter workload for your server.</p> | ||
| 208 | |||
| 209 | <pre><code>postconf -M "smtp/inet=smtp inet n - y - 1 postscreen" | ||
| 210 | postconf -M "smtpd/pass=smtpd pass - - y - - smtpd" | ||
| 211 | postconf -P "smtpd/pass/content_filter=spamassassin" | ||
| 212 | postconf -M "tlsproxy/unix=tlsproxy unix - - y - 0 tlsproxy" | ||
| 213 | postconf -M "dnsblog/unix=dnsblog unix - - y - 0 dnsblog" | ||
| 214 | postconf -e "postscreen_dnsbl_sites = zen.spamhaus.org" | ||
| 215 | postconf -e "postscreen_dnsbl_action = enforce" | ||
| 216 | postconf -e "postscreen_greet_action = enforce" | ||
| 217 | </code></pre> | ||
| 218 | |||
| 219 | <h3>Submission over TLS (submissions)</h3> | ||
| 220 | <p>Submission over TLS (aka submissions) is the process you will use to submit | ||
| 221 | mail to your server from a mail client. These commands configure submissions to | ||
| 222 | use a fully-encrypted session, as opposed to STARTTLS, and to only allow access | ||
| 223 | to authenticated clients.</p> | ||
| 224 | |||
| 225 | <pre><code>postconf -M "submissions/inet=submissions inet n - y - - smtpd" | ||
| 226 | postconf -P "submissions/inet/smtpd_tls_wrappermode=yes" | ||
| 227 | postconf -P "submissions/inet/smtpd_tls_security_level=encrypt" | ||
| 228 | postconf -P "submissions/inet/smtpd_tls_auth_only=yes" | ||
| 229 | postconf -P "submissions/inet/smtpd_sasl_auth_enable=yes" | ||
| 230 | postconf -P "submissions/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject" | ||
| 231 | postconf -P "submissions/inet/smtpd_helo_restrictions=" | ||
| 232 | postconf -P "submissions/inet/smtpd_sender_restrictions=reject_sender_login_mismatch" | ||
| 233 | postconf -P "submissions/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject" | ||
| 234 | postconf -P "submissions/inet/syslog_name=postfix/submissions" | ||
| 235 | postconf -P 'submissions/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1' | ||
| 236 | postconf -P 'submissions/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'</code></pre> | ||
| 237 | |||
| 238 | <h3>OPTIONAL - submission with mandatory STARTTLS</h3> | ||
| 239 | <p>Having configured submission over TLS on port 465 this step is optional. | ||
| 240 | STARTTLS is considered by some to be less secure than full-session TLS and | ||
| 241 | may be vulnerable to exploitation.</p> | ||
| 242 | |||
| 243 | <pre><code>postconf -M "submission/inet=submission inet n - y - - smtpd" | ||
| 244 | postconf -P "submission/inet/smtpd_tls_security_level=encrypt" | ||
| 245 | postconf -P 'submission/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1' | ||
| 246 | postconf -P 'submission/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1' | ||
| 247 | postconf -P "submission/inet/smtpd_sasl_auth_enable=yes" | ||
| 248 | postconf -P "submission/inet/smtpd_tls_auth_only=yes" | ||
| 249 | postconf -P "submission/inet/syslog_name=postfix/submission" | ||
| 250 | postconf -P "submission/inet/smtpd_helo_restrictions=" | ||
| 251 | postconf -P "submission/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject" | ||
| 252 | postconf -P "submission/inet/smtpd_helo_restrictions=" | ||
| 253 | postconf -P "submission/inet/smtpd_sender_restrictions=reject_sender_login_mismatch" | ||
| 254 | postconf -P "submission/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject" | ||
| 255 | </code></pre> | ||
| 256 | |||
| 257 | <h3>SpamAssassin Configuration</h3> | ||
| 258 | <p>Finally, this command tells Postfix how to interact with SpamAssassin.</p> | ||
| 259 | |||
| 260 | <pre><code>postconf -M "spamassassin/unix=spamassassin unix - n n - - pipe user=debian-spamd argv=/usr/bin/spamc --socket=/var/run/spamd.sock -e /usr/sbin/sendmail -oi -f \${sender} \${recipient}" </code></pre> | ||
| 261 | |||
| 262 | <h2>Dovecot Configuration</h2> | ||
| 263 | <p>Dovecot configuration is usually split up into many different files under | ||
| 264 | <strong>/etc/dovecot/conf.d/</strong> but here will be doing all of the | ||
| 265 | configuration in the primary config file | ||
| 266 | <strong>/etc/dovecot/dovecot.conf</strong>. Open that file with your editor | ||
| 267 | of choice, clear all of its contents, and then replace it with the following.</p> | ||
| 268 | |||
| 269 | <pre><code><em># /etc/dovecot/conf.d/10-auth.conf</em> | ||
| 270 | disable_plaintext_auth = yes | ||
| 271 | auth_username_format = %n | ||
| 272 | auth_mechanisms = plain | ||
| 273 | userdb { | ||
| 274 | driver = passwd | ||
| 275 | } | ||
| 276 | passdb { | ||
| 277 | driver = pam | ||
| 278 | } | ||
| 279 | |||
| 280 | <em># /etc/dovecot/conf.d/10-mail.conf</em> | ||
| 281 | mail_location = maildir:~/Mail:INBOX=~/Mail/Inbox:LAYOUT=fs | ||
| 282 | namespace inbox { | ||
| 283 | type = private | ||
| 284 | prefix = | ||
| 285 | separator = / | ||
| 286 | inbox = yes | ||
| 287 | subscriptions = yes | ||
| 288 | list = yes | ||
| 289 | } | ||
| 290 | |||
| 291 | <em># /etc/dovecot/conf.d/10-master.conf</em> | ||
| 292 | service imap-login { | ||
| 293 | # Run login processes in high-security mode (see: LoginProcess.txt in dovecot docs) | ||
| 294 | service_count = 1 | ||
| 295 | # Disable unencrypted IMAP by setting port for plain IMAP to 0 | ||
| 296 | inet_listener imap { | ||
| 297 | port = 0 | ||
| 298 | } | ||
| 299 | inet_listener imaps { | ||
| 300 | port = 993 | ||
| 301 | ssl = yes | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | <em># Allow postfix to use dovecot SASL</em> | ||
| 306 | service auth { | ||
| 307 | unix_listener /var/spool/postfix/private/auth { | ||
| 308 | mode = 0660 | ||
| 309 | user = postfix | ||
| 310 | group = postfix | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | <em># /etc/dovecot/conf.d/10-ssl.conf</em> | ||
| 315 | ssl = required | ||
| 316 | ssl_key = </etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem | ||
| 317 | ssl_cert = </etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem | ||
| 318 | ssl_client_ca_dir = /etc/ssl/certs | ||
| 319 | ssl_dh = </usr/share/dovecot/dh.pem | ||
| 320 | |||
| 321 | <em># Mozilla intermediate compatibility</em> | ||
| 322 | ssl_min_protocol = TLSv1.2 | ||
| 323 | ssl_cipher_list = ECDHE+ECDSA+AESGCM:ECDHE+aRSA+AESGCM:ECDHE+ECDSA+CHACHA20:ECDHE+aRSA+CHACHA20:DHE+aRSA+AESGCM:!aNULL:!eNULL | ||
| 324 | |||
| 325 | ssl_prefer_server_ciphers = yes | ||
| 326 | ssl_client_require_valid_cert = yes | ||
| 327 | |||
| 328 | |||
| 329 | <em># /etc/dovecot/conf.d/15-lda.conf</em> | ||
| 330 | protocol lda { | ||
| 331 | mail_plugins = \$mail_plugins sieve | ||
| 332 | } | ||
| 333 | |||
| 334 | <em># /etc/dovecot/conf.d/15-mailboxes.conf</em> | ||
| 335 | namespace inbox { | ||
| 336 | mailbox Sent { | ||
| 337 | special_use = \Sent | ||
| 338 | auto = subscribe | ||
| 339 | } | ||
| 340 | mailbox Trash { | ||
| 341 | special_use = \Trash | ||
| 342 | auto = create | ||
| 343 | autoexpunge = 30d | ||
| 344 | } | ||
| 345 | mailbox Drafts { | ||
| 346 | special_use = \Drafts | ||
| 347 | auto = subscribe | ||
| 348 | } | ||
| 349 | mailbox Spam { | ||
| 350 | special_use = \Junk | ||
| 351 | auto = create | ||
| 352 | autoexpunge = 30d | ||
| 353 | } | ||
| 354 | mailbox Archive { | ||
| 355 | special_use = \Archive | ||
| 356 | auto = create | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | <em># /etc/dovecot/conf.d/20-imap.conf</em> | ||
| 361 | imap_capability = +SPECIAL-USE | ||
| 362 | |||
| 363 | <em># /etc/dovecot/conf.d/90-sieve.conf</em> | ||
| 364 | plugin { | ||
| 365 | sieve = ~/.dovecot.sieve | ||
| 366 | sieve_default = /var/lib/dovecot/sieve/default.sieve | ||
| 367 | sieve_global = /var/lib/dovecot/sieve/ | ||
| 368 | }</code></pre> | ||
| 369 | |||
| 370 | <p>Then create the default sieve filtering script at | ||
| 371 | <strong>/var/lib/dovecot/sieve/default.sieve</strong></p> | ||
| 372 | <pre><code>require ["fileinto", "mailbox"]; | ||
| 373 | /* | ||
| 374 | * Discard mail that has a spam score greater than or equal to 10 | ||
| 375 | */ | ||
| 376 | if header :contains "X-Spam-Level" "**********" { | ||
| 377 | discard; | ||
| 378 | stop; | ||
| 379 | } | ||
| 380 | /* | ||
| 381 | * Discard messages marked as infected by a virus scanner | ||
| 382 | */ | ||
| 383 | if header :contains "X-Virus-Scan" "infected" { | ||
| 384 | discard; | ||
| 385 | stop; | ||
| 386 | } | ||
| 387 | /* | ||
| 388 | * If message is marked as spam (and falls below discard threshold) put into spam mailbox | ||
| 389 | */ | ||
| 390 | if header :contains "X-Spam-Flag" "YES" { | ||
| 391 | fileinto "Spam"; | ||
| 392 | }</code></pre> | ||
| 393 | |||
| 394 | <p>And compile the script</p> | ||
| 395 | |||
| 396 | <pre><code>sievec /var/lib/dovecot/sieve/default.sieve</code></pre> | ||
| 397 | |||
| 398 | |||
| 399 | <p>Finally, configure PAM authentication for dovecot at | ||
| 400 | <strong>/etc/pam.d/dovecot</strong>. Append these changes leaving any include | ||
| 401 | statements intact.</p> | ||
| 402 | <pre><code>auth required pam_unix.so | ||
| 403 | account required pam_unix.so</code></pre> | ||
| 404 | |||
| 405 | <h2>OpenDKIM</h2> | ||
| 406 | <p>DKIM is a mail-verification method that cryptographically signs mail | ||
| 407 | to allow receivers to verify the authenticity of the sender. Our mail server | ||
| 408 | will use DKIM to validate signatures on incoming mail and sign outgoing mail. DKIM | ||
| 409 | requires a public key to be published via DNS, which will be done near the end of | ||
| 410 | the guide.</p> | ||
| 411 | |||
| 412 | <p>Start by generating the DKIM key</p> | ||
| 413 | |||
| 414 | <pre><code>opendkim-genkey -D /etc/dkimkeys -d <em>example.com</em> -s mail | ||
| 415 | chown opendkim: /etc/dkimkeys/* | ||
| 416 | chmod 600 /etc/dkimkeys/* | ||
| 417 | mv /etc/dkimkeys/mail.private /etc/dkimkeys/mail.pem</code></pre> | ||
| 418 | |||
| 419 | <p>Here we make a directory for the opendkim socket inside the postfix chroot and | ||
| 420 | make it accessible to the postfix user.</p> | ||
| 421 | |||
| 422 | <pre><code>mkdir /var/spool/postfix/opendkim | ||
| 423 | chmod 770 /var/spool/postfix/opendkim | ||
| 424 | chown opendkim:opendkim /var/spool/postfix/opendkim | ||
| 425 | usermod -aG opendkim postfix</code></pre> | ||
| 426 | |||
| 427 | <p>Edit the configuration file at <strong>/etc/opendkim.conf</strong> | ||
| 428 | to be as follows:</p> | ||
| 429 | |||
| 430 | <pre><code>On-BadSignature reject | ||
| 431 | On-Security reject | ||
| 432 | Syslog yes | ||
| 433 | SyslogSuccess yes | ||
| 434 | LogResults yes | ||
| 435 | Canonicalization simple | ||
| 436 | Mode sv | ||
| 437 | OversignHeaders From | ||
| 438 | Domain <strong>example.com</strong> | ||
| 439 | Selector mail | ||
| 440 | KeyFile /etc/dkimkeys/mail.pem | ||
| 441 | UserID opendkim | ||
| 442 | UMask 007 | ||
| 443 | Socket local:/var/spool/postfix/opendkim/opendkim.sock | ||
| 444 | PidFile /run/opendkim/opendkim.pid | ||
| 445 | TemporaryDirectory /run/opendkim | ||
| 446 | InternalHosts 127.0.0.1 | ||
| 447 | TrustAnchorFile /usr/share/dns/root.key | ||
| 448 | RequireSafeKeys True | ||
| 449 | AlwaysAddARHeader True | ||
| 450 | </code></pre> | ||
| 451 | |||
| 452 | <h2>OpenDMARC</h2> | ||
| 453 | <p>DMARC is another mail-verification technology that provides verification of the | ||
| 454 | address seen by end-users and either or both of SPF and DKIM. | ||
| 455 | |||
| 456 | <p>Like with OpenDKIM, we need to make a directory inside the postfix chroot | ||
| 457 | for the socket and assign proper permissions.</p> | ||
| 458 | <pre><code>mkdir /var/spool/postfix/opendmarc | ||
| 459 | chmod 770 /var/spool/postfix/opendmarc | ||
| 460 | chown opendmarc:opendmarc /var/spool/postfix/opendmarc | ||
| 461 | usermod -aG opendmarc postfix | ||
| 462 | </code></pre> | ||
| 463 | |||
| 464 | <p>Now we write the configuration file at <strong>/etc/opendmarc.conf</strong></p> | ||
| 465 | |||
| 466 | <pre><code>PidFile /run/opendmarc/opendmarc.pid | ||
| 467 | PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat | ||
| 468 | RejectFailures True | ||
| 469 | Socket local:/var/spool/postfix/opendmarc/opendmarc.sock | ||
| 470 | Syslog True | ||
| 471 | SyslogFacility mail | ||
| 472 | UMask 002 | ||
| 473 | UserID opendmarc | ||
| 474 | HistoryFile /var/run/opendmarc/opendmarc.hist | ||
| 475 | SPFIgnoreResults True | ||
| 476 | SPFSelfValidate True | ||
| 477 | </code></pre> | ||
| 478 | |||
| 479 | <p>Then create the history file and set permissions.</p> | ||
| 480 | |||
| 481 | <pre><code>touch /var/run/opendmarc/opendmarc.hist | ||
| 482 | chown opendmarc:opendmarc /var/run/opendmarc/opendmarc.hist | ||
| 483 | chmod 664 /var/run/opendmarc/opendmarc.hist | ||
| 484 | </code></pre> | ||
| 485 | |||
| 486 | <p>Now that both OpenDKIM and OpenDMARC are configured we can define them as milters | ||
| 487 | in postfix. This will tell postfix to route mail through one or both of these milters | ||
| 488 | depending on whether it is incoming or outgoing.</p> | ||
| 489 | |||
| 490 | <pre><code>postconf -P "smtpd/pass/smtpd_milters=unix:opendkim/opendkim.sock,unix:opendmarc/opendmarc.sock" | ||
| 491 | postconf -P "submissions/inet/smtpd_milters=unix:opendkim/opendkim.sock" | ||
| 492 | <em># If you enabled submission on port 587 run this too</em> | ||
| 493 | postconf -P "submission/inet/smtpd_milters=unix:opendkim/opendkim.sock" | ||
| 494 | </code></pre> | ||
| 495 | |||
| 496 | <h2>Postgrey</h2> | ||
| 497 | <p>Postgrey implements a spam-filter technique known as greylisting, which | ||
| 498 | always rejects mail on the first try and for a period of time afterwards known | ||
| 499 | as the greylist period. The idea behind this being that legitimate senders will | ||
| 500 | send the mail again later, while spammers, in a rush to send as many messages as | ||
| 501 | possible before being blacklisted, will not.</p> | ||
| 502 | |||
| 503 | <p>Postgrey ships with an extensive whitelist domains that are known | ||
| 504 | to cause issues (mainly large providers that constantly send from different | ||
| 505 | addresses). This whitelist file is located at | ||
| 506 | <strong>/etc/postgrey/whitelist_clients</strong> and can be appended to include | ||
| 507 | any domain you do not wish to be subject to greylisting.</p> | ||
| 508 | |||
| 509 | <p>The configuration needed here is minimal, just open | ||
| 510 | <strong>/etc/default/postgrey</strong> and make these changes</p> | ||
| 511 | |||
| 512 | <pre><code>POSTGREY_OPTS="--unix=/var/spool/postfix/private/postgrey --privacy" | ||
| 513 | POSTGREY_TEXT="Greylisted - see https://www.greylisting.org"</code></pre> | ||
| 514 | |||
| 515 | <p>And then enable the service</p> | ||
| 516 | |||
| 517 | <pre><code>systemctl enable --now postgrey</code></pre> | ||
| 518 | |||
| 519 | <h2>Policyd-SPF</h2> | ||
| 520 | <p>SPF is yet another mail-verification technology that uses DNS records to | ||
| 521 | delegate specific servers as being authorized to send mail for the domain | ||
| 522 | (and implicitly all other servers as unauthorized). Policyd-SPF will perform | ||
| 523 | SPF checking of received mail and reject mail that fails SPF verfication.</p> | ||
| 524 | |||
| 525 | <p>First, tell postfix how to access Policyd-SPF</p> | ||
| 526 | |||
| 527 | <pre><code>postconf -e "policyd-spf_time_limit = 3600" | ||
| 528 | postconf -M "policyd-spf/unix=policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf"</code></pre> | ||
| 529 | |||
| 530 | <p>And then edit the configuration file at | ||
| 531 | <strong>/etc/postfix-policyd-spf-python/policyd-spf.conf</strong></p> | ||
| 532 | |||
| 533 | <pre><code>debugLevel = 1 | ||
| 534 | TestOnly = 1 | ||
| 535 | HELO_reject = Fail | ||
| 536 | Mail_From_reject = Fail | ||
| 537 | Header_Type = AR | ||
| 538 | <em># These settings increase false-positive risk</em> | ||
| 539 | <em># Comment them if you want to reduce that risk</em> | ||
| 540 | PermError_reject = True | ||
| 541 | TempError_Defer = True</code></pre> | ||
| 542 | |||
| 543 | |||
| 544 | <h2>SpamAssassin</h2> | ||
| 545 | <p>SpamAssassin is a spam-filter that will scan all received mail and assign | ||
| 546 | a spam score based on configured rules. SpamAssassin is much heavier and more | ||
| 547 | resource-intensive than any of the previous spam-filtering/verification programs | ||
| 548 | we have configured. The postfix spam-filtering philosophy emphasizes the use | ||
| 549 | of lightweight checks before passing to an external content filter such as | ||
| 550 | SpamAssassin. Ideally, non-legitimate mail will have already been caught by one | ||
| 551 | of the previous methods, and SpamAssassin will only have to operate on a much | ||
| 552 | smaller subset of the mail that is sent to our server.</p> | ||
| 553 | |||
| 554 | <p>We have actually already told postfix to use SpamAssassin as a content filter | ||
| 555 | so in this section we just need to edit the configuration file | ||
| 556 | <strong>/etc/spamassassin/local.cf</strong>.</p> | ||
| 557 | |||
| 558 | <pre><code><em># Clearly indicate message is spam to user</em> | ||
| 559 | rewrite_header Subject *****SPAM***** | ||
| 560 | rewrite_header From *****SPAM***** | ||
| 561 | |||
| 562 | <em># Set required score to be marked as spam, 5.0 is default.</em> | ||
| 563 | <em># Lower to make policy more strict or raise to be more lenient.</em> | ||
| 564 | required_score 5.0 | ||
| 565 | |||
| 566 | <em># Attach original messages as text/plain instead of message/rfc822 to spam reports</em> | ||
| 567 | report_safe 2 | ||
| 568 | |||
| 569 | <em>Do not implicitly trust mail based on IP address except localhost</em> | ||
| 570 | trusted_networks 127.0.0.1/32 | ||
| 571 | </code></pre> | ||
| 572 | |||
| 573 | <p>And finally make a few changes to the defaults file at | ||
| 574 | <strong>/etc/default/spamassassin</strong></p> | ||
| 575 | |||
| 576 | <pre><code>OPTIONS="--listen /var/run/spamd.sock --max-children 5" | ||
| 577 | PIDFILE=/var/run/spamd.pid | ||
| 578 | CRON=1</code></pre> | ||
| 579 | |||
| 580 | <h2>Wrapping Up</h2> | ||
| 581 | <p>At this point we have done all of the necessary configuration of the mail | ||
| 582 | server programs. We have just a few more minor tasks before your mail server | ||
| 583 | is operational.</p> | ||
| 584 | |||
| 585 | <h3>Configure Firewall</h3> | ||
| 586 | <p>We need to open the proper ports in the firewall. This example uses UFW.</p> | ||
| 587 | |||
| 588 | <pre><code>ufw allow 25 comment "smtp" | ||
| 589 | ufw allow 465 comment "submission over TLS" | ||
| 590 | <em># Run this next command only if you enabled submission on port 587</em> | ||
| 591 | ufw allow 587 comment "mail submission" | ||
| 592 | ufw allow 993 comment "IMAP over TLS" | ||
| 593 | ufw reload</code></pre> | ||
| 594 | |||
| 595 | <h3>Restart services</h3> | ||
| 596 | <p>Now let's restart the services to pick up any configuration changes.</p> | ||
| 597 | |||
| 598 | <pre><code>systemctl restart postfix | ||
| 599 | systemctl restart dovecot | ||
| 600 | systemctl restart opendkim | ||
| 601 | systemctl restart opendmarc | ||
| 602 | systemctl enable --now spamassassin | ||
| 603 | systemctl restart spamassassin | ||
| 604 | systemctl restart postgrey</code></pre> | ||
| 605 | |||
| 606 | <h3>DNS Entries</h3> | ||
| 607 | <p>Finally, we needs to set some required DNS records to enable mail flow and | ||
| 608 | verification. Begin by logging into your registrar or DNS host and editing | ||
| 609 | your DNS records.</p> | ||
| 610 | |||
| 611 | <h3>A Record</h3> | ||
| 612 | <p>If you did not set a wildcard A record earlier, you will need to set one now | ||
| 613 | for <strong>mail</strong>. | ||
| 614 | Alternatively, if you are running the mail server on the same server as your | ||
| 615 | website, you may want to instead make a CNAME record pointing mail to www.</p> | ||
| 616 | |||
| 617 | <h3>MX Record</h3> | ||
| 618 | <p>MX records tell servers attempting to send you mail where to send it. Open the | ||
| 619 | MX records section on your registrar and add a new record. An MX | ||
| 620 | record consists of a priority and a destination. Set the priority to 10 and the | ||
| 621 | destination to <strong>mail</strong>, or whatever your subdomain for this mail | ||
| 622 | server is. The host value can be left blank or may need to be set to "@" | ||
| 623 | depending on your registrar.</p> | ||
| 624 | |||
| 625 | <h3>DKIM TXT Record</h3> | ||
| 626 | <p>Now we will set the three TXT records we need. Open the TXT records tab on | ||
| 627 | your registrar.</p> | ||
| 628 | |||
| 629 | <p>We'll set the DKIM record first. The command we ran to | ||
| 630 | generate our DKIM keys also generates a DNS record for us which will be helpful | ||
| 631 | here. Print that to the screen with:</p> | ||
| 632 | |||
| 633 | <pre><code>cat /etc/dkimkeys/mail.txt</code></pre> | ||
| 634 | |||
| 635 | <p>You should get a lengthy output that looks something like the following. The | ||
| 636 | bolded portion is the value.</p> | ||
| 637 | |||
| 638 | <pre><code>mail._domainkey IN TXT ( <strong>"v=DKIM1; h=sha256; k=rsa; " | ||
| 639 | "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWw" | ||
| 640 | "bA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</strong> ) ; ----- DKIM key mail for example.com</code></pre> | ||
| 641 | |||
| 642 | <p>You can cleanup the spacing of the value as your registrar should automatically | ||
| 643 | handle any needed splitting of the record. The parts you need to paste into your | ||
| 644 | registrar's web interface should then look like this.</p> | ||
| 645 | |||
| 646 | <pre><code><em># Name/Host</em> | ||
| 647 | mail._domainkey | ||
| 648 | <em># TXT Value</em> | ||
| 649 | "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWwbA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</code></pre> | ||
| 650 | |||
| 651 | <h3>DMARC TXT Record</h3> | ||
| 652 | <p>The DMARC record should be as follows:</p> | ||
| 653 | |||
| 654 | <pre><code><em># Name/Host</em> | ||
| 655 | _dmarc | ||
| 656 | <em># Value</em> | ||
| 657 | "v=DMARC1; p=reject; rua=mailto:dmarc@<strong>example.com</strong>; fo=1" | ||
| 658 | </code></pre> | ||
| 659 | |||
| 660 | <h3>SPF Record</h3> | ||
| 661 | <p>Your SPF record will look like this. Remember to replace | ||
| 662 | <strong>mail.example.com</strong> with your server name.</p> | ||
| 663 | |||
| 664 | <pre><code><em># Name/Host</em> | ||
| 665 | @ | ||
| 666 | <em># Value</em> | ||
| 667 | "v=spf1 a:<strong>mail.example.com</strong> -all" | ||
| 668 | </code></pre> | ||
| 669 | |||
| 670 | <h3>PTR Record</h3> | ||
| 671 | <p>Many mail servers rely on PTR records for verification purposes so we need | ||
| 672 | to make sure our server's IP address resolves to the proper domain name. If | ||
| 673 | your mail server is residing on a VPS, you will need to add this record on your | ||
| 674 | VPS provider's interface, consult their documentation for details.</p> | ||
| 675 | |||
| 676 | <h2>Creating your own Mail User</h2> | ||
| 677 | <p>Your mail server is now up and running. Let's create an email for you to | ||
| 678 | receive mail.</p> | ||
| 679 | |||
| 680 | <pre><code>useradd --shell /usr/sbin/nologin --create-home --user-group <strong>user</strong> | ||
| 681 | echo "<strong>user@example.com user</strong>" >> /etc/postfix/login_maps | ||
| 682 | echo "<strong>user user</strong>" >> /etc/postfix/local_maps | ||
| 683 | postmap /etc/postfix/login_maps | ||
| 684 | postmap /etc/postfix/local_maps | ||
| 685 | postfix reload | ||
| 686 | </code></pre> | ||
| 687 | |||
| 688 | <p>I have a script available for adding and removing users that you can find | ||
| 689 | <a href=https://git.chudnick.com/mail-tools/tree/server/mailadm>here</a>. | ||
| 690 | |||
| 691 | <h3>Connecting From a Mail Client</h3> | ||
| 692 | <p>When connecting your account to a mail client you need to use these settings.</p> | ||
| 693 | |||
| 694 | <ul> | ||
| 695 | <li>Username: <strong>user@example.com</strong> </li> | ||
| 696 | |||
| 697 | <li>Password: the password for <strong>user@example.com</strong> </li> | ||
| 698 | |||
| 699 | <li>Server name: <strong>mail.example.com</strong> </li> | ||
| 700 | |||
| 701 | <li>IMAP Port: 993</li> | ||
| 702 | |||
| 703 | <li>IMAP Connection: SSL/TLS</li> | ||
| 704 | |||
| 705 | <li>SMTP Port: 465</li> | ||
| 706 | |||
| 707 | <li>SMTP Connection Type: SSL/TLS</li> | ||
| 708 | |||
| 709 | </ul> | ||
| 710 | <p> | ||
| 711 | <hr> | ||
| 712 | Consider <a href=../donate.html>donating</a> if this article was useful. | ||
| 713 | <a class=qr href=../images/bitcoin.png>[BTC]</a> | ||
| 714 | </p> | ||
| 715 | </main> | ||
| 716 | <footer> | ||
| 717 | <a href=../kb.html>Knowledge Base</a> | ||
| 718 | <br> | ||
| 719 | <a href=../index.html>www.chudnick.com</a> | ||
| 720 | </footer> | ||
| 721 | </body> | ||
| 722 | </html> | ||
| 723 | |||
