1 Securing a Postfix Smtp Server
2 ==============================
3 :author: Aaron Ball
4 :email: nullspoon@iohq.net
5
6
7 == {doctitle}
8
9 I must start this post with the acknowledgement that I know only what I've
10 experienced on this topic.
11
12 I recently set up my own mail server for the fun of it. I figured it was
13 something I'd never done, so why not, right?
14
15 Well, one day later, spammers discovered my server and began using it to send
16 out spam mail (curse you spammers!). I didn't notice this until I received a
17 notification from my hosting provider that my network IO was over the threshold
18 I had set. I promptly logged in, tailed the mail logs and discovered
19 unbelievable amounts of mail being rejected by Google, Yahoo, Aol, and Hotmail.
20 Why? Spam.
21
22 With that, I spent the next day figuring out how to better secure my smtp
23 server. I'd like to detail some of the exploits that the spammers used to get
24 in to my server, how I failed in configuring my server properly, and how I
25 fixed it.
26
27 [[leaving-an-open-relay]]
28 Leaving an Open Relay
29 ~~~~~~~~~~~~~~~~~~~~~
30
31 An open relay is basically an smtp server that requires no authentication
32 and/or allows connections from outside ip addresses, so anyone can send emails
33 from anywhere to anywhere. The settings in question specific to this issue in
34 my configuration were the following:
35
36 ----
37 smtpd_recipient_restrictions = permit_mynetworks, check_relay_domains
38 ...
39 mynetworks = 0.0.0.0/0 127.0.0.0/8 [::fff:127.0.0.0]/104 [::1]/128
40 ----
41
42 Basically that is an open relay. Here's why.
43
44 * Firstly, *smtpd_recipient_restrictions = permit_mynetworks* allows any
45 email to be sent without any restrictions as long as the email originated
46 from a box in the IP ranges specified in the mynetworks variable.
47
48 * Secondly, *mynetworks = 0.0.0.0/0* allows emails to be sent through my
49 smtp server from any client within the ip range of 0.0.0.0-255.255.255.255.
50 This is bad because any computer can try to send emails through my smtp
51 server and succeed because of the permit_mynetworks restriction (or lack
52 therof).
53
54 [[specifying-incorrect-configuration-parameters]]
55 Specifying Incorrect Configuration Parameters
56 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57
58 One of my first mistakes when configuring Postfix was misspelling some smtpd
59 parameters using smtp_ instead of smtpd_ to prefix them. As it turns out, if
60 you do this, Postfix ignores your attempted configuration without a peep. This
61 one went on for a long time before I noticed that two of my smtpd_ fields were
62 missing the 'd'. As soon as I put those in there, everything started working as
63 it should, albeit still insecure, but at least it was following the
64 specifications of my config file.
65
66
67 [[not-specifying-a-correct-smtpd_sasl_path]]
68 Not Specifying a Correct smtpd_sasl_path
69 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70
71 This one took me a while. The *smtpd_sasl_path* is a path to the socket file
72 for your SASL server. In my case, this is Dovecot.
73
74 As it turns out, Postfix defaults to running in chroot mode which makes its
75 root directory /var/spool/postfix/. This was my first mistake. I was specifying
76
77 ----
78 smtpd_sasl_path = /var/spool/postfix/private/auth-client
79 ----
80
81 and it was not starting up because it couldn't find the socket file. This was
82 because it was looking for the file at
83 /var/spool/postfix/var/spool/postfix/private/auth-client a path which clearly
84 does not exist. The solution to this is to simply specify a relative path.
85
86 ----
87 smtpd_sasl_path = private/auth-client
88 ----
89
90 I decided that I would get smart though and shave off some text from the field
91 value by configuring Dovecot to place the socket file at
92 /var/spool/postfix/auth-client rather than at
93 /var/spool/postfix/private/auth-client (speaking in absolute terms despite
94 running in chroot mode). This returned the following error
95
96 ----
97 warning: when SASL type is "dovecot", SASL path "auth-client" should be a socket pathname
98 ----
99
100 As it turns out, postfix won't operate with the SASL socket file path outside
101 of the private directory. So with that, I placed my auth-client file back in
102 the private directory and Postfix started up fine.
103
104
105 [[not-specifying-the-allowed-senders-file]]
106 Not Specifying the Allowed Senders File
107 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
108
109 Even if you do have authentication required, you still need to specify which
110 users can send email with what addresses. This was a bit of a surprise to me
111 initially because I was under the impression that a password is associated with
112 an email address, not an email address(s) associated with a username and
113 password. To keep users from being able to send email as addresses that are not
114 theirs (specifically randomly generated addresses in my case), you need to
115 create a mapping file that maps usernames to the addresses they are authorized
116 to send mail as. In my case, this is a one to one relationship (one address per
117 username). Before my example I'd like to note that the filename is not
118 required to be the one I use (though my filename is the one used in the Postfix
119 setup documentation).
120
121 Okay. Let's create the map file. To do this, open up and edit
122 /etc/postfix/controlled_envelope_senders (this file likely doesn't exist yet)
123
124 ----
125 vim /etc/postfix/controlled_envelope_senders
126 ----
127
128 Once you've got that open, you simply need to put the maps in there.
129
130 ----
131 # envelope sender owners jcricket@example0.com jimminey
132 ----
133
134 Now that we've done that, we need to turn it into a binary. Run the following
135 command and it will generate a <filename>.db binary map file in the same
136 directory as the original file.
137
138 ----
139 postmap /etc/postfix/controlled_envelope_senders
140 ----
141
142 Presto! Now the user jimminey can send email as jcricket@example0.com. However,
143 so can everyone else...still.
144
145 Now that we have our controlled envelope senders file, we need to reference it
146 in our postfix main.cf and set postfix up to restrict access to the maps
147 specified in that file. Crack er open in your favorite editor and put the
148 following line in somewhere after *smtpd_sasl_auth_enable*
149
150 ----
151 smtpd_sasl_auth_enable = yes
152 ...
153 # This line specifies our map file for use by postfix
154 # Note that this does NOT reference controlled_envelope_senders.db
155 smtpd_sender_login_maps = hash:/etc/postfix/controlled_envelope_senders
156 # This line sets postfix to reject anyone who authenticates but tries to send email as an address they aren't permitted to use
157 smtpd_recipient_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated, reject_unauth_destination
158 ----
159
160 So what we've just done is tell Postfix where our map file is
161 (smtpd_sender_login_maps). After that, we tell Postfix to reject any users that
162 have been authenticated but are trying to send with an address they aren't
163 authorized to send with in our map file (smtpd_recipient_restrictions). Please
164 note that *reject_sender_login_mismatch* comes at the beginning of the
165 smtpd_recipient_strictions field. This is key. It is so key in fact, that I
166 missed it (I only miss the key stuff of course thanks Murphy). This was the
167 forth exploit attempt that got me.
168
169
170 [[misordering-smtpd_recipient_restrictions]]
171 Misordering smtpd_recipient_restrictions
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
174 This one is the final bit that let the spammers in (so far at least).
175
176 The smtpd_recipient_restrictions are restrictions that you can place on
177 the users and their emails based on various things. In my case, I had
178 the following restrictions string
179
180 ----
181 smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_sender_login_mismatch, reject_unauth_destination
182 ----
183
184 Postfix applies these restrictions in the order in which they are specified. As
185 they put it <blockquote>Restrictions are applied in the order as specified; the
186 first restriction that matches wins.</blockquote> As soon as one restriction
187 matches, then the ones that follow don't get applied. This was very
188 problematic because in my case permit_mynetworks is first. So that I can log
189 in from my cell phone which has an IP address that changes, I set
190
191 ----
192 mynetworks = 0.0.0.0/0 127.0.0.0/8 [::fff:127.0.0.0]/104 [::1]/128
193 ----
194
195 which allows any IP address to connect to my SMTP server. Since Postfix takes
196 the first match and goes no further and any IP address is in 0.0.0.0/0, anyone
197 can send mail through my SMTP server. This = bad.
198
199 What you should do is start your restrictions with the the most strict
200 restrictions followed by the less strict. In my case, that looks like
201
202 ----
203 smtpd_recipient_restrictions = reject_sender_login_mismatch, permit_sasl_authenticated, reject_unauth_destination
204 ----
205
206 In the event someone tries to send an email, first they must login. If they
207 don't log in, they are rejected due to reject_sender_login_mismatch (we can't
208 do a match if we don't have a sender username). Secondly, once logged in, the
209 user must be authorized to use the address they are trying to send as as
210 specified in the smtpd_sender_login_maps line. Finally, once the user has been
211 authenticated and they have permissions to use the address they are trying to
212 send as, their email is not rejected. It follows that they are then filtered
213 through permit_sasl_authenticated. This basically runs a check to see if they
214 are authenticated (which we know they already are because of the previous
215 filter) and since they are, they are permitted and Postfix stops looking for
216 more matches because it's found one that permits the user to perform their
217 requested action.
218
219 As chef Elzar says, "Bam!"
220
221
222 Category:Linux
223 Category:Postfix
224
225
226 // vim: set syntax=asciidoc:
|