Thursday, June 10, 2010
Wednesday, June 9, 2010
Setting up DKIM, SPF, Domainkeys DNS, Regular DNS on CentOS
I'm working with one site that is struggling with mail being rejected outright by other mail servers and sent to the spam box. Over the past month, I've fixed a few errors in the MIME formats, contacted rejecting domains (aol, live, yahoo, etc), all of which has increased slightly the success rate on mail delivery. But not enough. So the next step has been to setup the signature pages so that the site can sign mail and no spammers can take advantage of the good reputation the site builds. This is no guarantee, of course, but it's the next logical thing to try.
The first step was to setup postfix. Although I had managed to get sendmail running fairly well, it was not fun. Adding in the DKIM signatures would have probably been even less fun. So I switched the server over to using postfix, which was easy as a charm. (http://wiki.centos.org/HowTos/postfix)
Because the site needs to be able to sign mail from POP clients for exactly one username, I set up that user with no login shell, but I configured postfix to accept mail from that single user. Any other users or domains - relay rejected. I tried to set that SMTP connection up as a secure connection to protect the password etc (http://www.linuxmail.info/smtp-authentication-postfix-centos-5/) but ran into a couple stumbling blocks and for the moment it's not as important to continue with that - I might look into it later again. I did not turn on pop/imap - only smtp - and only for that one user with no login shell so I'm not too stressed about the plaintext password. (Did I mention that I backup all my sites every hour?)
Next, SPF. The 'Sender Policy Framework" is a record that the domain name owner can publish that gives receiving spam-sorters an idea about who you want to be able to send mail from your domain. (See http://www.openspf.org/Project_Overview, http://www.zytrax.com/books/dns/ch9/spf.html, http://www.openspf.org/Test_Suite, and http://tools.bevhost.com/spf/) In my case, I made a very restrictive spf "v=spf1 a -all" that allows mail to be sent only from the main domain example.com.
Following these two guides, I added dkim to the postfix configuration:
http://www.howtoforge.com/set-up-dkim-on-postfix-with-dkim-milter-centos-5.2
Unfortunately, at that point, I learned from emailing with name.com support that my domain name registrar name.com does not support the <>._domainkey txt record... so I was forced to set up my own nameserver.
After some comparisons, I chose to setup named/bind. (http://www.howtoforge.com/creating-your-own-webserver-with-bind-and-apache-centos5) I am running it chroot'ed (http://www.wains.be/index.php/2007/12/13/centos-5-chroot-dns-with-bind/), and I'm running it only as the master zone for my domain example.com, having turned off the normal nameserver functionality of reciprocal queries. Here's the syntax for the zone file: http://centos.org/docs/5/html/5.2/Deployment_Guide/s2-bind-zone-directives.html. Another good dns/bind reference.
All this amounted to more than one day's work, because it was my first time setting up these systems. Unfortunately, the mail was still receiving dkim=permerror (better than the dkim=neutral from before... as long as it doesn't last forever!) ... which I found was in part due to my server's time being *15 minutes slow* (dig the dryptic message dkim=permerror (verification error: signature timestamp in the future) !! ) -- So to solve this problem once and for all, "yum install ntp ; chkconfig ntpd on ; date ; ntpdate pool.ntp.org ; date ; service ntpd start"
voila - emails doubly signed by SPF and DKIM. Verified by sending to google email address (then show detailed headers) and also by sending to
- sa-test@sendmail.net
- autorespond+dkim@dk.elandsys.com .
Final step was adding in additional domainkey authentication to make yahoo particularly happy. It's almost identical to dkim. Yahoo, being in the dark ages, still uses it. So http://www.topdog.za.net/postfix_domainkeys_milter
You'll have to add multiple milters in the configuration of postfix to handle both dkim and domainkeys. Here's the end of my postfix config file:
smtpd_milters = inet:localhost:20209 unix:/var/run/dk-milter/dk.sock
non_smtpd_milters = inet:localhost:20209 unix:/var/run/dk-milter/dk.sock
The one thing that I did not yet address is that we are now supposed to publish SPF both in the TXT field and in the SPF field. With my version of named/bind, I am getting an unknown RR type when I include the SPF in my zone file, so for now I have commented it out. Any tips on what might be the cause of that? Google definitely gets the "terrible results" prize for that query, as it is overvaluing the old specification documents rather than any recent, useful discussion of the error.
Finally, you also need to investigate the history of your specific IP and get your reverse DNS information set properly. To investigate some aspects of your IP address and its characteristics, take a look at Microsoft's Smart Network data services (for delivery to live/hotmail), http://www.senderbase.org/, and Yahoo http://feedbackloop.yahoo.net. Good luck.
REFERENCES
http://palma-seo.com/setting-dkim-spf-domainkeys-dns-bind
HOWTO define DKIM/ADSP RRs
DomainKeys Identified Mail (DKIM) has a similar goal to SPF in that it provides a way for mail recipients to verify that mail, purporting to come from domain X (and possibly user Y), did, indeed, come from domain X (and possibly user Y). DKIM works by having one, or more, sending or handling mail agent(s) (MUA/MTA) cryptographically sign the mail (by adding a DKIM-Signature header to each mail item). Any receiving (or intermediate mail handling) MTA/MUA can authenticate the source of the mail and optionally add an Authenticated-Results header (defined in RFC 5451). DKIM mail signing uses public-key (or asymmetric) cryptography to create digital signatures covering defined mail headers and/or the mail body. The public key(s) used in signature verification are stored in the DNS in DKIM TXT RRs (described below).
If, like most normal humans, you are cryptographically challenged then you might find our crypto primer useful. Then again you may not.
In addition to the DKIM TXT RR, the DKIM specifications allows the domain owner to define an Author Domain Signing Policies (ADSP) TXT RR which essentially provides advice to the validating mail receiver about what to do if a mail item is not signed.
DKIM is defined by a series of RFCs of which RFC 4871 and RFC 5672 define the DNS DKIM TXT RR format (as well as the added mail headers), RFC 5617 defines DNS Author Domain Signing Policies (ADSP) TXT RR formats for indicating signing practises and RFC 5585 describes how it all works. Serious stuff - if somewhat wordy.
Only that part of DKIM concerned with the DNS is described here. It is beyond the scope of this document to detail all the functionality offered by DKIM and readers are advised to consult the various listed RFCs for all the gory details. We make the following observations from the RFCs:
DKIM uses "identifiers" (typically an email address or just a domain name) not IP addresses (unlike SPF) as its base for authentication. Mail content is verified not the path it takes.
Clearly bad guys could equally use DKIM to sign their email. The various DKIM RFCs emphasize that DKIM only authenticates the mail source and needs to be used in conjunction with, say, a whitelist (or other reputation system) to allow decisions to be made about accepting or rejecting DKIM signed mail.
DKIM does not provide mail confidentiality (encryption).
DKIM digital signatures can, optionally, be used to provide mail integrity.
DKIM does not require purchase of SSL certificates. The public keys are obtained directly from the DNS of the authenticating domain and may be generated using Open Source (or other) tools.
Whether the above points are positive or negative will depend entirely on the implementor's context and requirements.
Many of the values in the DKIM TXT RR will depend on those defined for the mail signer software. While creating this documentation we used OpenDKIM as a reference source which supports sendmail and postfix through the milter interface. Many other DKIM implementations exist and you are advised to carefully read your mail system's DKIM documentation.
A number of major email organization have already implemented DKIM, perhaps most notably google's gmail.
DKIM DNS RR Format
DKIM uses (at the present time) a TXT RR to contain all the DNS stored data. The generic format of the TXT RR is:
name ttl class rr text
DKIM TXT RR Format - Name
There may be one or more DKIM TXT RRs for any given domain. The generic name format for the DKIM TXT RR is:
name ttl class rr text
;DKIM TXT RR format is
selector._domainkey ttl class rr DKIM-specific-text
The content of the DKIM-specific-text field is defined below. The name of each TXT RR used to contain DKIM data must match that constructed by the validating email receiver which extracts values contained in the DKIM-Signature mail header field. Specifically, the validating email receiver will construct this name by extracting the selector (s= tag-value field, defined in RFC 4871 Section 3.5), appending the fixed subdomain name _domainkey and finally appending the extracted domain name (d= tag-value field, defined in RFC 4871 Section 3.5).
The selector is, in essence, a unique and relatively arbitrary tag whose purpose/format is defined in RFC 4871 Section 3.1.
DKIM Scope and Selectors
While it was noted above that description of the full functionality of DKIM is beyond the scope of this document an understanding of the delegation concept, its scope and relationship to the selector field is required to fully understand some of the fields that populate the DKIM DNS RR(s). What follows is our attempt to explain this concept insofar as it relates to the use of the DKIM TXT RR.
In part, the relative complexity of DKIM relates to the designers' objective to allow mail from any domain to be handled by various parties, for example, while user@example.com may normally send mail through a company mail service (MTA) the same user, using the same email address may also wish to send mail from home via an ISP's MTA. Equally, bulk mailing may be delegated to an external third party. Other such scenarios may be imagined. In all such scenarios DKIM allows for one, or more, third parties to be delegated the signing responsibility for some, or all, of the mail using a particular domain name.
Configuring DKIM could be done by the domain owner generating a DKIM public-private key pair for use with one or more mail addresses or subdomains, supplying the private key to the delegated signer and publishing the public key in a DKIM TXT RR in the domain's zone file under a suitable name. Conversely, the delegated signer could generate the DKIM public/private key pair and supply the public key to the domain name operator for inclusion in a DKIM TXT RR under a suitable name.
So just what is the suitable DKIM TXT RR name? This problem is solved using the selector field (s= in DKIM-Signature header) and the domain field (d= in the DKIM-Signature mail header). The suitable name is computed by the validating receiver as described above. Thus if the selector in the DKIM-Signature mail header (s=) is joe and the domain name (from the d= field of the DKIM-Signature mail header) is example.com then the constructed query name will be joe._domainkey.example.com This is a relatively trivial illustration and a number of additional examples are provided. By querying the originating domain's DNS using the constructed name a validating receiver can obtain information including, crucially, the public key to be used to authenticate the mail.
By default the signer will sign mail for the domain and all its subdomains - meaning that a single DKIM TXT RR can be created to cover the entire domain. Mail sent from user@example.com and user@sub.example.com will use the same selector and hence use the same key.
Where the domain owner wishes to use unique keys for subdomains (or where subdomains are known not to exist) the domain owner should set the 's' flag of the t= tag in the DKIM TXT RR for the domain. In this case separate DKIM TXT RRs (and ADSP RRs) will be required for each subdomain that can send mail (See Examples).
DKIM TXT RR Format - Text
The text part of the DKIM TXT RR can contain a number of semi-colon (;) separated tag=value fields (defined in RFC 4871 Section 3.6.1). The following section documents the allowed tags and values (a number of examples are provided to show scenario specific RR values).
Note: DKIM uses a tag=value notation to define fields in both the DKIM-Signature header and the DNS TXT RR text field. Somewhat confusingly, in a number of cases the tag name part, such as v= or s=, will take the same value for both the DKIM-Signature mail header and the DNS RR. In some case the meaning will be the same but the valid values may be different, in other cases the meaning of the tag is different for each entity. Readers are advised to ensure they consult the correct section of the specification. Specifically for DKIM-Signature mail header tag=value pairs use RFC 4871 Section 3.5 (updated by RFC 5672) and for DNS TXT RR tag=value pairs use RFC 4871 Section 3.6.1.
| v= (version) | ||||
Optional. Defines the DKIM version number and may only (at this time) take the (defaulted) value DKIM1. While it may be safely omitted our advice is to include it. v=DKIM1; | ||||
| g= (granularity) | ||||
Optional. Granularity defines the range of user (local) part of the email (everything to the left hand side of the @) to which this DKIM TXT RR applies. A single wild card (*) value may be used anywhere in the field. Defaults to g=*(all user - local - part addresses match). This value (after any wild card processing) must exactly match the mail From: user (local) part. The only case we have seen where it could make some sense is if you have a single email address in the domain. Doubtless there are other cases. However, assuming you are not doing anything too fancy (good luck if you are) it may be safely omitted. # single email address form | ||||
| h= (hash algorithm) | ||||
Optional. Defines one or more colon (:) separated hash (digest) algorithms that will be used for the purpose of creating digital signatures (in conjunction with k= below) covering either or both of the defined mail headers or the mail body (including, optionally, MIME attachments). Allowable values are from the set sha1 and sha256. Default is h=* (all). Since all implementations of DKIM are mandated to support both sha1 and sha256 hash (digest) algorithms it may be safely omitted. h=sha1:sha256; | ||||
| k= (key type) | ||||
Optional. Defines the public key algorithm being used. Defaults to k=rsa. Since rsa is the only algorithm currently supported it may be safely omitted. k=rsa; | ||||
| n= (notes) | ||||
Optional. Defines human readable (text) than may be used by validating receiver administrators. Unless this imparts significant, perhaps world-stopping, knowledge it may be safely omitted. n=Don't trust these guys; | ||||
| p= (public key material) | ||||
Defines the public key (in base64 text format) for the algorithm defined by the k= tag whose private key was used to digitally sign user defined parts of the mail item. The data for the public key may be created by openssl using the following command sequence (taken from RFC 4871 Appendix C and reproduced here only for convenience): # Create the RSA public private key pair Remove the lines beginning with "-" and edit the remaining text in any of the following formats (most key material replaces with ' ... ' for brevity): ; single line format See TXT RR for additional information on layout and formatting of text. If a key is to be revoked (declared invalid) then setting the p= tag to a null value will achieve this: p=; | ||||
| s= (service type) | ||||
Optional. Defines the service type to which DKIM is applied. At this time the only valid value is email but clearly the designers had their sights set on greater goals. The default is s=* (all). Since email is, currently, the only DKIM supported service it may be safely omitted. s=email; | ||||
| t= (flags) | ||||
Optional. Defaults to no flags set. A colon (:) separated list of flags to be used by the validator. Two flags are currently defined:
t=y:s; |
A number of worked examples are provided.
ADSP TXT RR Format
The Author Domain Signing Practices (ADSP) TXT RR is designed to allow a domain to indicate its mail signing policies. The ADSP TXT RR is optional but the ADSP policies may be used to assist a validating receiving MTA in determining how to handle mail that is not signed. The format of the ADSP TXT RR is:
name ttl class rr text
;ADSP TXT RR format is
_adsp._domainkey ttl class rr ADSP-specific-text
Only one ADSP TXT RR per domain may be defined - however each subdomain may also have its own ADSP TXT RR. See examples for more detail.
ADSP TXT RR Format - Text
The ADSP TXT RR text field uses the same tag=value format used throughout DKIM. The allowed tags and their corresponding values are:
| Tag | Values | ||||||
| dkim= | A single value from the following set of permissible values is allowed:
dkim=discardable; |
Note: There are a number of additional tag=value pairs mentioned in various RFC drafts (which have no official status) and also in OpenDKIM documentation. The most interesting is an r=error-address tag=value pair which defines the local part of an email address to which extended error information may be sent. Thus if r=ouch; is present for the domain example.com then mail regarding any validation failures will be sent to ouch@exmple.com. The precise status (that is, will it work) of this tag=value pair is unknown (Jan 2010).
Examples
All domains are assumed to use the ubiquitous domain example.com unless otherwise stated. The public key material is denoted by blah...blah for simplicity and brevity.
All Mail Signed - One MTA, No Subdomains
The tightest and simplest scenario assumes that all mail for the domain is sent using a single path - typically an in-house MTA. No subdomains are used in email addresses. All the mail is signed and users working from home or remotely will use, say, a webmail interface to the in-house MTA. Email from any other source is deemed to be invalid. A single selector may be used in this instance, which we will call mail since we entirely lack imagination:
; zone example.com fragment
...
mail._domainkey IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp._domainkey IN TXT "dkim=discardable;"
; if you like typing you could have written
mail._domainkey.example.com. IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp._domainkey.example.com. IN TXT "dkim=discardable;"
; OR you could use an $ORIGIN
$ORIGIN _domainkey
mail IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp IN TXT "dkim=discardable;"
; if RRs appear below this $ORIGIN then it will have to be reset
Notes:
The DKIM TXT RR name of mail is entirely arbitrary we could, just as easily, have called it gobbledegook (though that is longer and we can't always spell it correctly) and is the selector for the domain example.com. The selector is defined using either the Selector directive or a KeyTable for OpenDKIM.
Since all mail is signed the _adsp RR uses the super macho discardable value, if you want to be weasely use all or even unknown.
Since the domain does not send mail using any subdomains the t=s flag allows the validating receiver to be tighter in its handling by rejecting any mail from a subdomain. If subdomains are used remove the entire t= tag.
The v=DKIM1; tag could be omitted and will default to the defined value. We believe it is always good practise to indicate which version of any specification you think you are supporting so we have included it. In 5 years no-one will remember. Or, if you are like us, in 2 weeks time no-one will remember.
All other tags are left to their default values (and no notes are supplied!).
Loose DKIM Signing
For use during testing or for those not entirely sure what their mail users actually do - including whether they use subdomains in their mail addresses.
; zone example.com fragment
...
hope._domainkey IN TXT "v=DKIM1;t=y;p=blah....blah;"
_adsp._domainkey IN TXT "dkim=unknown;"
; if you like typing you could have written
hope._domainkey.example.com. IN TXT "v=DKIM1;t=y;p=blah....blah;"
_adsp._domainkey.example.com. IN TXT "dkim=unknown;"
; OR you could use an $ORIGIN
$ORIGIN _domainkey
hope IN TXT "v=DKIM1;t=y;p=blah....blah;"
_adsp IN TXT "dkim=unknown;"
; if RRs appear below, $ORIGIN may have to be reset
Notes:
The DKIM TXT RR name hope is entirely arbitrary we could, just as easily, have called it pray (both names faithfully reflect usage at this stage) and is the selector for the domain example.com. The selector is defined using either the Selector directive or a KeyTable for OpenDKIM.
Since mail may, or may not, be signed the _adsp RR must use the unknown value.
The t=y flag indicates to the validating receiver that we would like as much help as possible (verbose, highly detailed, error messages hopefully) if anything goes wrong with any mail that we do, finally, get around to signing. Since we don't actually know if our users use subdomains it is not safe to use the s flag. If, however, we were positive about this one fact then we could use a flags field of t=y:s; and live dangerously.
Multiple Subdomain DKIM Signing
Assume we send mail from the domain example.com and two subdomains, maillist.example.com (signed by external third party) and secure.example.com (signed using in-house MTA). We always sign mail from the subdomains but not always the main domain.
; zone example.com fragment
...
; DKIM and ADSP TXT RR for main domain
$ORIGIN _domainkey
domain IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp IN TXT "dkim=unknown;"
; if other RRs appear below, $ORIGIN may have to be reset
; DKIM and ADSP for maillist subdomain
$ORIGIN _domainkey.maillist
external IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp IN TXT "dkim=discardable;"
; if other RRs appear below, $ORIGIN may have to be reset
; DKIM and ADSP for secure subdomain
$ORIGIN secure
internal IN TXT "v=DKIM1;t=s;p=blah....blah;"
_adsp IN TXT "dkim=discardable;"
REFERENCES
http://www.zytrax.com/books/dns/ch9/dkim.html
; if RRs appear below, $ORIGIN may have to be reset
Notes:
The DKIM TXT RRs names domain, external and internal are entirely arbitrary we could, just as easily, have called them alice, bob and uncle-bert and are the selectors for each of the separately signed part of mail from the domain example.com. Note: use of these selector values is partly deliberate to re-enforce the point that there is no necessary relationship between subdomain names and selector names. A single selector is defined in the Selector directive of OpenDKIM or if multiple selectors are required they must be defined in an OpenDKIM KeyTable.
We use $ORIGIN directives in this scenario because we like them and think they make the subsequent definitions much clearer (and shorter as well).
We use the t=s flag in all the DKIM TXT RR definitions because we have explicitly defined a key for use with each used subdomain (only maillist and secure) so explicitly need to notify the validating receiver that each key has no subdomain scope.
Since we have no idea about signing from the main example.com domain (as we defined in scenario description) we use the unknown value, whereas since we know that the maillist and secure domain will always be signed we have used discardable.
When using OpenDKIM in this scenario the values example.com, maillist.example.com and secure.example.com must all appear in either a Domains directive or a SigningTable, in both cases a SubDomains No directive must be used.
HOWTO - Define an SPF Record
http://www.spfwizard.com/
For yahoo: http://help.yahoo.com/l/us/yahoo/mai...r%2Findex.html
for gmail: http://mail.google.com/support/bin/r...pe=msgdelivery
for hotmail: http://postmaster.live.com/
This section defines HOWTO configure a Sender Policy Framework (SPF) record for a domain and its mail servers. DKIM/ADSP is another approach involving the signing of mail to authenticate the sender domain.
SPF was initiated by Meng Weng Wong of pobox.com to enable validation of legitimate sources of email for a domain and is now an IETF standard (RFC 4408).
Briefly, the design intent of the SPF record is to allow a receiving MTA (Message Transfer Agent) to interrogate the Name Server of the domain which appears in the email (the sender) and determine if the originating IP of the mail (the source) is authorized to send mail for the sender's domain. The mail sender is required to publish an SPF RR (documented here) but the sending MTA is unchanged.
The SPF information SHOULD be defined in a standard TXT RR and MAY now be defined in an SPF RR type (BIND releases from 9.4.0 support the SPF RR type - see also RFC 4408).
If a SPF (TXT) RR exists and authorizes the source IP address the mail can be accepted by the MTA. If the SPF (TXT) RR does not authorize the IP address the mail can be bounced - it did not originate from an authorized source for the sender's domain.
Many Open Source MTAs have already been modified to use the SPF record and there is no down-side (assuming you get the SPF RR right) and plenty of potential up-side (elimination/reduction of some spam categories) to implement the SPF record now.
We use the following terminology to try and simplify the descriptions below:
- sender - the full email address of the originator of the mail item (typically uses return-path in the actual SPF checks)
- source-ip - the IP address of the SMTP server trying to send this message
- sender-domain the domain name part of the sender's email address e.g. assume the sender is info@example.com the sender-domain is example.com.
The SPF record defines one or more tests to carry out to verify the sender. Each test returns a condition code (pre below). The first test to pass will terminate SPF processing.
TXT RR Format
The standard TXT and SPF record format is defined as:
name ttl class TXT text
name ttl class SPF text
The SPF RR is functionally identical to a TXT record with SPF data. BIND 9.4+ supports the SPF RR type, however previous versions, and most other DNS software (as of July 2007), do not yet support the SPF RR type. Thus the RFC's recommendation is to always provide a TXT based SPF RR and, if your DNS software supports the SPF RR type, duplicate the information from the TXT version of the SPF RR in a native SPF RR. The reason for this procedure is simply because while the master/slave may support the SPF RR, querying name servers - such as name servers used by receiving MTAs - may not. Some, but not all examples, below have been updated to reflect the use of both record types to illustrate usage. In all cases the TXT and SPF RRs are shown with a comment line between containing the word AND as a reminder of the current policy recommendation. It is safe to assume for the foreseeable future that only using a TXT version of the SPF will always work.
The SPF data is entirely contained in the text field (a quoted string). SPF defines the contents of the quoted string as follows:
v=spf1 [[pre] type ] ... [mod]
Where:
v=spf1 | ||||||||||||||
| Mandatory. Defines the version being used. Currently the only version supported is spf1. | ||||||||||||||
pre | ||||||||||||||
Optional (defaults to +). pre defines the code to return when a match occurs. If a test is conclusive either add + or omit (defaults to +). If a test might not be conclusive use "?" or "~" (tilde). "-"(minus) is typically only used with -all to indicate that if we have had no previous matches - fail.
| ||||||||||||||
type | ||||||||||||||
Defines the mechanism type to use for verification of the sender. May take one of the following values: Basic MechanismsThese types do NOT define a verification mechanism but affect the verification sequence.
Sender MechanismsThese types define a verification mechanism.
| ||||||||||||||
mod | ||||||||||||||
Two optional record modifiers are defined. If present they should follow the last type directive i.e. after the all. The current values defined are as follows:
|
Macro-Expansion
SPF defines a number of macro-expansion features as defined below:
Note: all macro-expansion delimiters use braces {}.
| Modifier | Description |
| %{c} | Only allowed in TXT records referenced by the exp field. The IP of the receiving MTA. |
| %{d} | The current domain, normally the sender-domain %{o} but replaced by the value of any domain argument in the type above. |
| %{h} | The domain name supplied on HELO or EHLO, normally the hostname of the sending SMTP server. |
| %{i} | sender-ip The IP of SMTP server sending mail for user info@example.com. |
| %{l} | replace with local part of sender e.g. if sender is info@example.com, the local part is info. |
| %{o} | The sender-domain e.g. if email address is info@example.com the sender-domain is example.com. |
| %{p} | The validated domain name. The name obtained using the PTR RR of the sender-ip. Use of this macro will require an additional query unless a ptr type is used. |
| %{r} | Only allowed in TXT records referenced by the exp field. The name of the host performing the SPF check. Normally the same as the receiving MTA. |
| %{t} | Only allowed in TXT records referenced by the exp field. Current timestamp. |
| %{s} | Replace with sender email address, for instance, info@example.com |
| %{v} | Replaced with "in-addr" if sender-ip is an IPv4 address and "ip6" if an IPv6 address. Used to construct reverse map strings. |
The above macros may take one or more additional arguments as follows:
r - Indicates reverse the order of the field, for instance, %{or} would display example.com as com.example and %{ir} would display 192.168.0.2 as 2.0.168.192. The normal split uses "." (dot) as the separator but any other character may be used to define the split but a "." (dot) is always used when rejoining so, for instance, %{sr@} would display info@example.com as example.com.info.
digit - the presence of a digit (range 1 to 128) limits the number of right most elements displayed, for instance, %{d1} displays only com only from example.com but %{d5} would display five right hand elements up to the maximum available, in this case it will display example.com since that is all that is available.
Examples
Example 1
Example 1: Assumes a single mail server which both sends and receives mail for the domain.
; zone file fragment for example.com
$ORIGIN example.com.
IN MX 10 mail.example.com.
....
mail IN A 192.168.0.4
; SPF stuff
; domain SPF
example.com. IN TXT "v=spf1 mx -all"
; AND
example.com. IN SPF "v=spf1 mx -all"
; mail host SPF
mail IN TXT "v=spf1 a -all"
; AND
mail IN SPF "v=spf1 a -all"
Notes:
- the domain SPF is returned from a sender-domain query using the sender's email address e.g. sender = info@example.com sender-domain = example.com. The SPF record only allows the MX host to send for the domain.
- the mail host SPF is present in case the receiving MTA uses a reverse query to obtain the source-ip host name and then does a query for the SPF record of that host. The SPF record states that the A record of mail.example.com alone is permitted to send mail for the domain.
If the domain contains multiple MX servers the domain SPF would stay the same but each mail host should have a SPF record.
Example 2
Example 2: Assumes the domain will send mail through an offsite mail server e.g. an ISP:
; zone file fragment for example.com
$ORIGIN example.com.
IN MX 10 mail.offsite.com.
....
; SPF stuff
; domain SPF
example.com. IN TXT "v=spf1 include:offsite.com -all"
; AND
example.com. IN SPF "v=spf1 include:offsite.com -all"
; WARNING: offsite.com MUST have a valid SPF definition
Notes:
- This format should be used IF AND ONLY IF you know that offsite.com has a valid SPF configuration.
- include recurses (restarts) verification using the SPF records for offsite.com. Mail configuration changes are localised at offsite.com which may simplify administration.
- include could have been replaced with redirect.
Example 3
Example 3: Assumes we are the host for a number of virtual mail domains and that we can send mail from any host in our subnet.
Zone file fragment for one of the virtual mail domains:
; zone file fragment for vhost1.com
$ORIGIN example.com.
IN MX 10 mail.example.com.
....
; SPF stuff
; domain SPF
vhost1.com. IN TXT "v=spf1 include:example.com -all"
; AND
vhost1.com. IN SPF "v=spf1 include:example.com -all"
Notes:
- the domain SPF is returned from a sender-domain query using the sender's email e.g. sender = info@vhost1.com, sender-domain = vhost1.com. The SPF record recurses to the DOMAIN example.com for verification.
Zone file for example.com
; zone file fragment for example.com
IN MX 10 mail.example.com.
....
; SPF stuff
; domain SPF - any host from
; 192.168.0.1 to 192.168.0.30 (32 - bcast and mcast = 30)
; can send mail
example.com. IN TXT "v=spf1 ip4:192.168.0.3/27 -all"
; AND
example.com. IN SPF "v=spf1 ip4:192.168.0.3/27 -all"
; mail SPF
mail IN TXT "v=spf1 ip4:192.168.0.3/27 -all"
; AND
mail IN SPF "v=spf1 ip4:192.168.0.3/27 -all"
Notes:
- the domain SPF is returned from a sender-domain query using the sender's email e.g. sender = info@example.com sender-domain = example.com. The SPF record allows any host in the 32 address subnet which contains 192.168.0.3 to send mail for this and any host virtual domain e.g virtual1.com in the above example. NOTE: while /27 allows 32 IP addresses subnet rules remove 192.168.0.0 and 192.168.0.31 as the multicast and broadcast addresses respectively. [read more about IPv4 Classes]
- In the above scenario we could have used a slightly shorter version such as:
example.com. IN TXT "v=spf1 mx/27 -all"
This record has the same effect as a:192.168.0.3/27 above but will cost a further DNS look up operation whereas the IP is already available.
; AND
example.com. IN SPF "v=spf1 mx/27 -all" - The above scenario relies on the fact that customers will only send mail via the domain example.com i.e. they will NOT send via another ISP at home or when travelling. If you are not sure if this is the case you can terminate the sequence with ?all which says kinda pass (soft fail) and let the mail go through - perhaps logging the incident to capture statistics.
If the domain contains multiple MX servers the domain SPF would stay the same but each mail host should have a SPF record.
Example 4
Example 4: Assumes that the domain never sends mail from ANY location - ever. Typically you would do this to prevent bogus mail for everyone else - it is a supreme act of self-sacrifice!
; zone file fragment for example.com
; zone does NOT contain MX record(s)
...
; SPF stuff
; domain SPF
example.com. IN TXT "v=spf1 -all"
; AND
example.com. IN SPF "v=spf1 -all"
Notes:
- This SPF test will always fail since the only condition it tests is the all which results in a fail.
Example 5
Example 5: Illustrates various macro expansion features:
; zone file fragment for example.com
$ORIGIN example.org.
IN MX 10 mail.example.com.
....
; SPF records
; domain SPF
@ IN TXT "v=spf1 exists:%{ir}.%{v}.arpa -all exp=badguy.example.com"
; AND
@ IN SPF "v=spf1 exists:%{ir}.%{v}.arpa -all exp=badguy.example.com"
badguy IN TXT "The email from %{s} using SMTP server at %{i}
was rejected by %{c} (%{r}) at %{t} because it failed
the SPF records check for the domain %{p}.
Please visit http://abuse.example.com/badguys.html
for more information"
Notes:
- The badguy TXT above is split across multiple lines for presentation reasons only and should appear on a single line in the zone file.
- The exists:%{ir}.%{v}.arpa test is a great example BUT IT WILL NOT WORK because the exists type checks for an A RR whereas a reverse lookup is defined using a PTR RR. But it does show the power of macro-expansion and we could not think of a better one. However Stuart Gatham could, and suggested using the reversed IP address in a DNS Black List (DNSBL) as shown here:
@ IN TXT "v=spf1 exists:%{ir}.blacklist.example.com -all exp=badguy.example.com"You may also want to change the text in the badguy.example.com record to reflect the new failure. Many thanks for the suggestion.
; AND
@ IN SPF "v=spf1 exists:%{ir}.blacklist.example.com -all exp=badguy.example.com"
http://www.zytrax.com/books/dns/ch9/spf.html
Tuesday, June 8, 2010
Linux Kernel /etc/sysctl.conf Security Hardening
How do I set advanced security options of the TCP/IP stack and virtual memory to improve security and performance of my system? How do I configure Linux kernel to prevent certain kinds of attacks using /etc/sysctl.conf? How do I set Linux kernel parameters?
sysctl is an interface that allows you to make changes to a running Linux kernel. With /etc/sysctl.conf you can configure various Linux networking and system settings such as:
- Limit network-transmitted configuration for IPv4
- Limit network-transmitted configuration for IPv6
- Turn on execshield protection
- Prevent against the common 'syn flood attack'
- Turn on source IP address verification
- Prevents a cracker from using a spoofing attack against the IP address of the server.
- Logs several types of suspicious packets, such as spoofed packets, source-routed packets, and redirects.
sysctl command
The sysctl command is used to modify kernel parameters at runtime. /etc/sysctl.conf is a text file containing sysctl values to be read in and set by sysct at boot time. To view current values, enter:# sysctl -a
# sysctl -A
# sysctl mib
# sysctl net.ipv4.conf.all.rp_filter
To load settings, enter:# sysctl -p
Sample /etc/sysctl.conf
Edit /etc/sysctl.conf and update it as follows. The file is documented with comments. However, I recommend reading the official Linux kernel sysctl tuning help file (see below):
# The following is suitable for dedicated web server, mail, ftp server etc.
# ---------------------------------------
# BOOLEAN Values:
# a) 0 (zero) - disabled / no / false
# b) Non zero - enabled / yes / true
# --------------------------------------
# Controls IP packet forwarding
net.ipv4.ip_forward = 0
# Controls source route verification
net.ipv4.conf.default.rp_filter = 1
# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0
# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0
# Controls whether core dumps will append the PID to the core filename
# Useful for debugging multi-threaded applications
kernel.core_uses_pid = 1
# Controls the use of TCP syncookies
#net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2
########## IPv4 networking start ##############
# Send redirects, if router, but this is just server
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Accept packets with SRR option? No
net.ipv4.conf.all.accept_source_route = 0
# Accept Redirects? No, this is not router
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
# Log packets with impossible addresses to kernel log? yes
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Ignore all ICMP ECHO and TIMESTAMP requests sent to it via broadcast/multicast
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Prevent against the common 'syn flood attack'
net.ipv4.tcp_syncookies = 1
# Enable source validation by reversed path, as specified in RFC1812
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
########## IPv6 networking start ##############
# Number of Router Solicitations to send until assuming no routers are present.
# This is host and not router
net.ipv6.conf.default.router_solicitations = 0
# Accept Router Preference in RA?
net.ipv6.conf.default.accept_ra_rtr_pref = 0
# Learn Prefix Information in Router Advertisement
net.ipv6.conf.default.accept_ra_pinfo = 0
# Setting controls whether the system will accept Hop Limit settings from a router advertisement
net.ipv6.conf.default.accept_ra_defrtr = 0
#router advertisements can cause the system to assign a global unicast address to an interface
net.ipv6.conf.default.autoconf = 0
#how many neighbor solicitations to send out per address?
net.ipv6.conf.default.dad_transmits = 0
# How many global unicast IPv6 addresses can be assigned to each interface?
net.ipv6.conf.default.max_addresses = 1
########## IPv6 networking ends ##############
#Enable ExecShield protection
kernel.exec-shield = 1
kernel.randomize_va_space = 1
# TCP and memory optimization
# increase TCP max buffer size setable using setsockopt()
#net.ipv4.tcp_rmem = 4096 87380 8388608
#net.ipv4.tcp_wmem = 4096 87380 8388608
# increase Linux auto tuning TCP buffer limits
#net.core.rmem_max = 8388608
#net.core.wmem_max = 8388608
#net.core.netdev_max_backlog = 5000
#net.ipv4.tcp_window_scaling = 1
# increase system file descriptor limit
fs.file-max = 65535
#Allow for more PIDs
kernel.pid_max = 65536
#Increase system IP port limits
net.ipv4.ip_local_port_range = 2000 65000
How do I tune Linux VM subsystem?
How do I tune Linux network stack?
References:
BIND 9 Configure Views To Partition External and Internal DNS Information
How do I configure Bind 9 dns server views to allow a single nameserver in my DMZ to make different sets of data available to different sets of clients? For example, I'd like to run recursion, some other data for LAN users (192.168.1.0/24), and for the Internet user I'd like to display limited DNS data without recursion. How do I configure views to partition external (Internet) and internal (LAN) DNS information?
You need to edit /etc/named.conf or /var/named/chroot/etc/named.conf file, run (the following configuration is tested on FreeBSD and RHEL 5.x BIND 9 servers):# vi /var/named/chroot/etc/named.conf
Append the following and define internal subnet (192.168.1.0/24 and localhost with full access and recursion):
acl internal {
192.168.1.0/24;
localhost;
}; Define zone and other data as per your requirements:
//
// Lan zone recursion is the default
//
view "internal-view" {
match-clients { internal; };
zone "." IN {
type hint;
file "db.cache";
};
zone "internal.nixcraft.com " IN {
type master;
file "zones/lan.master.nixcraft.com";
allow-transfer { key TRANSFER; };
};
};
//
// external zone w/o recursion
//
view "external-view" {
match-clients { any; };
recursion no;
zone "nixcraft.com " IN {
type master;
file "zones/internet.master.nixcraft.com";
allow-transfer { key TRANSFER; };
};
};
Make sure you configure TSIG as described here.
Create Zone Files
First, create required directories, enter:# mkdir -p /var/named/chroot/var/named/zones
# chown named:named /var/named/chroot/var/named/zones
Create Internal Zone With LAN IP Data
Edit /var/named/chroot/var/named/zones/lan.master.nixcraft.com, run:# vi /var/named/chroot/var/named/zones/lan.master.nixcraft.com
Append the data, enter:
$ORIGIN nixcraft.com.
$TTL 3h
@ IN SOA ns1.nixcraft.com. vivek.nixcraft.com. (
20080703328 ; Serial yyyymmddnn
3h ; Refresh After 3 hours
1h ; Retry Retry after 1 hour
1h ; Expire after 1 week 1w
1h) ; Minimum negative caching of 1 hour
@ IN NS ns1.nixcraft.com.
@ IN NS ns2.nixcraft.com.
@ 3600 IN MX 10 mail1.nixcraft.com.
@ 3600 IN MX 20 mail2.nixcraft.com.
@ 3600 IN A 208.43.79.236
ns1 3600 IN A 208.43.138.52
ns2 3600 IN A 75.126.168.152
mail1 3600 IN A 208.43.79.236
mail2 3600 IN A 67.228.49.229
out-router 3600 IN A 208.43.79.100
; lan data
wks1 3600 IN A 192.168.1.5
wks2 3600 IN A 192.168.1.5
wks3 3600 IN A 192.168.1.5
in-router 3600 IN A 192.168.1.254
; add other lan specifc data below
Edit /var/named/chroot/var/named/zones/internet.master.nixcraft.com, run:# vi /var/named/chroot/var/named/zones/internet.master.nixcraft.com
Same as above but no internal data:
$ORIGIN nixcraft.com.
$TTL 3h
@ IN SOA ns1.nixcraft.com. vivek.nixcraft.com. (
20080703328 ; Serial yyyymmddnn
3h ; Refresh After 3 hours
1h ; Retry Retry after 1 hour
1h ; Expire after 1 week 1w
1h) ; Minimum negative caching of 1 hour
@ IN NS ns1.nixcraft.com.
@ IN NS ns2.nixcraft.com.
@ 3600 IN MX 10 mail1.nixcraft.com.
@ 3600 IN MX 20 mail2.nixcraft.com.
@ 3600 IN A 208.43.79.236
ns1 3600 IN A 208.43.138.52
ns2 3600 IN A 75.126.168.152
mail1 3600 IN A 208.43.79.236
mail2 3600 IN A 67.228.49.229
out-router 3600 IN A 208.43.79.100
Finally, reload data:# rndc reload
Test it, enter:$ ping in-router.nixcraft.com
$ ping out-router.nixcraft.com
Recommend readings:
- Bind Security: Transaction Signatures (TSIG) Configuration
- named.conf, and named man page
- BIND 9 Administrator Reference Manual
- http://www.cyberciti.biz/faq/linux-unix-bind9-named-configure-views/
Configure Apache MaxClients via WHM
We recently moved to a new server provider at work, and we soon noticed that at around 4pm each day the site would slow to a crawl. At this time of day we get in the region of 50-60 requests per second, but we have a beefy server so this shouldn’t be an issue.
The first request to each domain would sit for ages, anywhere up to 10 seconds, then subsequent requests would be in the order of a couple of hundred miliseconds. This lead me to think that there were no available processes on the server and the request was being queued.
Looking into this, I found that indeed Apache was reporting “150 requests currently being processed, 0 idle workers”.
I had a look at the mod_prefork settings, and they were set to the default values targetted to a much smaller site than ours. In the past I’d just have edited http.conf, rebooted and been sorted, but the new server uses WHM which means straight editing of the apache conf won’t stick – WHM will just overwrite any changes.
Having never used WHM before, I googled the issue and came accross a post advocating using the pre-main include to add these settings, however this does not work since the settings from there are over-written back to the measly defaults. Instead the changes need to be made in the pre-virtualhost include.
Here is a step-by-step guide to changing the relevant settings to allow your site to handle more concurrent clients.
- Log into WHM
- Under Service Configuration in the left menu, select Apache Configuration.
- Click Include Editor
- In the Pre-Virtualhost Include section, choose All Versions from the drop down.
- Enter your config settings. For our server I used the following which works for me:1
2
3
4
5
6
7<IfModule prefork.c>
StartServers 32
MinSpareServers 10
MaxSpareServers 30
ServerLimit 512
MaxClients 512
</IfModule>One thing to bear in mind here. ServerLimit must come before MaxClients! If you swap them you’ll find that you can never use more than 256 for MaxClients and you will get a warning from Apache:
1
2
3WARNING: MaxClients of 512 exceeds ServerLimit value of 256 servers,
lowering MaxClients to 256. To increase, please see the ServerLimit
directive. - Click the Restart Apache button to apply the new config
- You can check this is working by clicking Server Status > Apache Status. This should tell you how many requests are being processed. If all has gone well this will be a number less than your MaxClients setting!
One word of caution with this. You need to make sure that the MaxClients setting does not cause so many processes to be spawned that you run out of RAM and the OS starts to use swap space. You can check this by finding the amount of RAM an httpd process uses. I use the following command for this, which gives you the average size of all the httpd processes (obviously change “httpd” for whatever your Apache runs as):
1 | ps -ef | grep httpd | grep -v ^root | awk '{ print $2 '} | xargs pmap -d | grep ^mapped: | awk '{ print $4 }' | cut -dK -f1 | awk '{ SUM += $1} END { print SUM/NR"KB" }' |
Take this value and divide that into your RAM capacity, after allowing enough for other processes such as the OS and mysql etc. This will give you a rough idea how many apache processes you can afford to spawn. On modern hardware, generally you can run more than enough, but if you find you are causing swap to be used, you will either need to remove unnecessary modules to make the apache process smaller, lower the MaxClients or other memory-related directives, or else throw more hardware at the problem!
To give you an idea though, serving all 512 clients should use approx 1.2GB of RAM with my Apache setup.
REFERENCES
http://www.karlrixon.co.uk/articles/apache/configure-apache-maxclients-via-whm/
