├── DANE-for-SMTP-how-to.md
├── DKIM-how-to.md
├── DMARC-how-to.md
├── LICENSE-CC-BY-4.0.txt
├── README.md
├── SPF-how-to.md
├── TLS-config_spreadsheet
├── 20210506_TLS-config_NCSC-NL.ods
└── 20210506_TLS-config_NCSC-NL.xlsx
├── images
├── DANE-example-TLSA-record.png
├── dane-example-1-evilcert.png
├── dane-example-1-no-dane.png
├── dane-example-1-striptls.png
├── dane-example-1-with-dane.png
├── dane-halon-dnssec.png
├── dane-halon-inbound.png
├── dane-halon-outbound.png
├── dkim-halon-dns-record.png
├── dkim-halon-private-key.png
├── dkim-halon-public-key-details.png
└── logo-internetnl-en.svg
├── parked-domain-how-to.md
└── under construction
├── DNS-records-overview.md
├── Nginx-webserver-100%-example-config.md
└── STARTTLS-how-to.md
/DANE-for-SMTP-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # DANE for SMTP how-to
4 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [Internet.nl](https://internet.nl)) in cooperation with industry experts (hosters and vendors) and is meant to provide practical information and guidance on implementing DANE for SMTP.
5 |
6 | # Executive Summary
7 | * DANE is a best-practice technology for securing the transfer of email (SMTP) between organizations across the public Internet.
8 | * Successful DANE deployments require additional operational discipline.
9 | - Automated monitoring of your own email servers and related DNS records is a must.
10 | - Robust automation of coordinated DNS and email server certificate chain updates.
11 | - These topics will be covered in more detail in this how-to.
12 | * Please plan carefully and then deploy DANE for your email servers. Botched deployments not not only harm the domain in question, but also have a deterrent effect on adoption by others.
13 | * For more information on adoption statistics and software support, take a look at: [https://github.com/baknu/DANE-for-SMTP/wiki](https://github.com/baknu/DANE-for-SMTP/wiki)
14 |
15 | # Table of contents
16 | - [What is DANE?](#what-is-dane-)
17 | - [Why use DANE for SMTP?](#why-use-dane-for-smtp-)
18 | * [Risks of SMTP with opportunistic TLS](#risks-of-smtp-with-opportunistic-tls)
19 | * [DANE addresses these risks](#dane-addresses-these-risks)
20 | * [How about MTA-STS?](#how-about-mta-sts-)
21 | - [DANE TLSA record structure](#dane-tlsa-record-structure)
22 | - [Advantages of DANE explained by illustrations](#advantages-of-dane-explained-by-illustrations)
23 | * [Mail delivery: TLS without DANE](#mail-delivery--tls-without-dane)
24 | * [Mail delivery: TLS with MITM stripping TLS](#mail-delivery--tls-with-mitm-stripping-tls)
25 | * [Mail delivery: TLS with MITM using evil certificate](#mail-delivery--tls-with-mitm-using-evil-certificate)
26 | * [Mail delivery: TLS with DANE](#mail-delivery--tls-with-dane)
27 | * [Mail delivery: TLS with DANE without DNSSEC](#mail-delivery--tls-with-dane-without-dnssec)
28 | - [Reliable certificate rollover](#reliable-certificate-rollover)
29 | * [Points of attention when rolling over using "current + next"](#points-of-attention-when-rolling-over-using--current---next-)
30 | - [Tips, tricks and notices for implementation](#tips--tricks-and-notices-for-implementation)
31 | - [Inbound e-mail traffic (publishing DANE DNS records)](#inbound-e-mail-traffic--publishing-dane-dns-records-)
32 | * [Generating DANE records](#generating-dane-records)
33 | * [Publishing DANE records](#publishing-dane-records)
34 | * [Generating DANE roll-over records](#generating-dane-roll-over-records)
35 | * [Publishing DANE roll-over records](#publishing-dane-roll-over-records)
36 | - [Implementing DANE for SMTP on Postfix (inbound & outbound e-mail traffic)](#implementing-dane-for-smtp-on-postfix--inbound---outbound-e-mail-traffic-)
37 | * [Configuring Postfix](#configuring-postfix)
38 | - [Implementing DANE for SMTP on Exim (inbound & outbound e-mail traffic)](#implementing-dane-for-smtp-on-exim--inbound---outbound-e-mail-traffic-)
39 | * [Configuration for inbound e-mail traffic](#configuration-for-inbound-e-mail-traffic)
40 | + [Install or generate key pair](#install-or-generate-key-pair)
41 | + [Configure TLS](#configure-tls)
42 | * [Configuration for outbound e-mail traffic](#configuration-for-outbound-e-mail-traffic)
43 | + [DNSSEC validating resolvers](#dnssec-validating-resolvers)
44 | + [Configure DNSSEC validation in Exim](#configure-dnssec-validation-in-exim)
45 | + [Configure DANE](#configure-dane)
46 | - [Implementing DANE for SMTP on Halon (inbound & outbound e-mail traffic)](#implementing-dane-for-smtp-on-halon--inbound---outbound-e-mail-traffic-)
47 | * [Generic configuration](#generic-configuration)
48 | + [Install or generate key pair](#install-or-generate-key-pair-1)
49 | + [DNSSEC](#dnssec)
50 | * [Inbound](#inbound)
51 | * [Outbound](#outbound)
52 | + [Transport Label](#transport-label)
53 | + [Scripting](#scripting)
54 | + [Logging](#logging)
55 | - [Implementing DANE for SMTP on Port25 PowerMTA (outbound e-mail traffic)](#implementing-dane-for-smtp-on-port25-powermta--outbound-e-mail-traffic-)
56 | * [Generic configuration](#generic-configuration-1)
57 | + [Configure the use of DANE on the domain level](#configure-the-use-of-dane-on-the-domain-level)
58 | + [Logging](#logging-1)
59 | - [Special thanks](#special-thanks)
60 |
61 | Table of contents generated with markdown-toc
62 |
63 | # What is DANE?
64 | DANE is short for "**D**NS-based **A**uthentication of **N**amed **E**ntities" and is described in [RFC 6698](https://tools.ietf.org/html/rfc6698) with "updates and operational guidance" in [RFC 7671](https://tools.ietf.org/html/rfc7671). DANE establishes a downgrade-resistant method to verify an SMTP servers identity **before** it starts to transport an email message over a STARTTLS encrypted layer. In order to achieve this it uses verification information retrieved from the recipients DNSSEC signed DNS zone. DANE does not rely on additional trusted parties outside the delegation chain in DNS.
65 |
66 | DANE, as a method, has been designed to work with any TLS service. DANE for SMTP (which is described in [RFC 7672](https://tools.ietf.org/html/rfc7672)) implements the DANE method for SMTP. It is used increasingly and adds active attack (man-in-the-middle) resistance to SMTP transport encryption [RFC 7672 Section 1.3](https://tools.ietf.org/rfc7672#section-1.3). DANE for SMTP uses the presence of DNS TLSA ressource records to **securely signal TLS support** and to publish the means by which SMTP clients can successfully **authenticate legitimate SMTP servers**. The result is called "opportunistic DANE TLS", and resists downgrade and man-in-the-middle (MITM) attacks when the destination domain and its MX hosts are DNSSEC signed, and TLSA records are published for each MX host. While possible, DANE for HTTP is not presently supported by the major browsers and so has seen little deployment.
67 |
68 | # Why use DANE for SMTP?
69 | At this time it is not possible to require **mandatory** transport encryption (TLS) in public mail transport. A mail server might not support transporting messages using encryption. Today only plaintext or **opportunistic** transport encryption are applicable – opportunistic because it is up to the receiving server to decide if it wants to and is able to send messages using TLS (via STARTTLS).
70 |
71 | DANE offers several advantages by binding X.509 certificates to domains using DNSSEC. In an SMTP context this allows **a)** sending mail servers to securely signal TLS support by the receiving domain's mail server, and **b)** sending mail servers to verify the autenticity of the offered certificate by the receiving domain's mail server. This helps to address risks that occur when using oppurtunistic TLS connections between mail servers.
72 |
73 | ## Risks of SMTP with opportunistic TLS
74 | The way SMTP was designed including the usage of opportunistic TLS (via STARTTLS) is not without risks:
75 | * A sending server has no means to determine **beforehand** if the receiving server supports encrypted transport. Only after the communication has begun the sending server may learn from the features the receiving server supports that it allows for encrypted transport.
76 | * SMTP uses opportunistic TLS (via STARTTLS) as a mechanism to enable secure transport between mail servers. However, the fact that STARTTLS is opportunistic means that the initial connection from one mail server to another always starts unencrypted making it vulnerable to man in the middle attacks. If a mail server does not offer the 'STARTTLS capability' during the SMTP handshake (because it was stripped by an attacker), transport of mail occurs over an unencrypted connection.
77 | * Forcing the use of TLS for all mail servers would break backwards compatibility and is therefore not considered a viable option since it could seriously disrupt mail delivery across the internet.
78 | * By default mail servers do not validate the authenticity of another mail server's certificate; any random certificate is accepted (see [RFC 3207](https://tools.ietf.org/html/rfc3207)).
79 | - It was unclear which CAs to trust when validating the certificate for a given destination.
80 | - In MTA-to-MTA SMTP, server hostnames for the destination domain are obtained indirectly via DNS MX lookups, but, without DNSSEC, these names cannot be trusted. As a result, it was unclear which names to verify in the certificate.
81 |
82 | As as result, even when STARTTLS is used, a man in the middle attacker:
83 | * can strip the STARTTLS capability send by the receiving mail server because this communication takes place without the use of encryption.
84 | * can insert any certificate of his choice in the in the unencrypted communication and use it to intercept the traffic.
85 |
86 | ## DANE addresses these risks
87 | The risks of SMTP with opportunistic TLS can be mitigated by using DANE:
88 | * The presence of a DNS TLSA record for the receiving domain's mail server, should be interpreted by the sending mail server as the capability to use TLS. The use of TLS can therefore be forced when communicating with the receiving domain's mail server.
89 | * The operator of the receiving domain's mail server is obligated to ensure that any published TLSA records at all times match the server's certificate chain, and that STARTTLS is enabled and working. This makes it possible for the sending domain's mail server to validate the certificate offered by the receiving domain's mail server.
90 |
91 | In short: DANE allows sending mail servers to unconditionally require STARTTLS with a matching certificate chain. Otherwise, the sending mail server aborts the connection and tries another server or defers the message. Receiving servers with published TLSA records, are therefore no longer vulnerable to the afore mentioned man in the middle attacks.
92 |
93 | ## How about MTA-STS?
94 | Internet.nl currently does not include MTA-STS in its tests. This paragraph explains why this is the case.
95 |
96 | First you need to understand that, as [explained on our website](https://en.internet.nl/faqs/report/), the selection and development of tests performed by internet.nl is primairily based on:
97 | * the [‘comply-or-explain’ list](https://www.forumstandaardisatie.nl/open-standaarden) of the Dutch Standardisation Forum which is mandatory for all Dutch government agencies;
98 | * security advices of the Dutch NCSC;
99 | * relevant RFC’s of IETF.
100 |
101 | Final decisions in this area are made by our steering committee.
102 |
103 | MTA-STS is a fairly new standard for enforcing authenticated transport encryption between mail servers. The standard was developed primarily by the big cloud mail providers and seems to especially meet their specific needs. We have seen [quite some uptake of DANE](https://github.com/baknu/DANE-for-SMTP/wiki/4.-Adoption-statistics) (e.g. by Comcast, Protonmail and Cisco), but until now not by the major cloud mail providers. MTA-STS is relatively complex because it needs an extra HTTPS interface (including certificate validation) to function, and it is also considered to be less secure than DANE due to trust on first use and caching.
104 |
105 | MTA-STS relies on trust on first use (TOFU) and policy caching. So the initial connection is just STARTTLS (without authentication of the receiving mail server and without enforcing encryption). During that first connection the MTA-STS policy will be cached by the sending mail server and subsequent encrypted connections will be enforced and authenticated (until the policy cache time expires). This caching mechanism means the larger and more up up-to-date the cache, the better MTA-STS will work. Basically big (cloud) mail providers (that contact about every mail server worldwide each day) will have a large up-to-date policy cache, but smaller mail providers will not and therefore will run more often into trust on first use (i.e. initial less secure connection). So basically MTA-STS seems to offer better security to big players than to the smaller ones. In fact, the MTA-STS RFC [states](https://tools.ietf.org/html/rfc8461#section-10) that DANE offers better downgrade resistance.
106 |
107 | In view of the foregoing and considering the facts that the Dutch NCSC [advises](https://www.ncsc.nl/documenten/factsheets/2019/juni/01/factsheet-beveilig-verbindingen-van-mailservers) to use DANE for securing mail transport, and DNSSEC adoption in the Netherlands (and some other countries like .SE, .NO and .CZ) is quite high and keeps increasing, DANE and not MTA-STS is on the 'comply or explain' list and subsequently included on internet.nl.
108 |
109 | Note that MTA-STA and DANE can co-exists next to each other. They intentionally do not interfere.
110 |
111 | # DANE TLSA record structure
112 | 
113 |
114 | **Usage**: says something about the type of certificate that is used for this TLSA record.
115 | 0: PKIX-TA (not recommended / [not used for SMTP](https://tools.ietf.org/html/rfc7672#section-3.1.3))
116 | 1: PKIX-EE (not recommended / [not used for SMTP](https://tools.ietf.org/html/rfc7672#section-3.1.3))
117 | 2: DANE-TA: intermediate / root certificate (OK)
118 | 3: DANE-EE: end-entity certificate (also called 'host certificate' or 'server certificate') (OK)
119 |
120 | **Selector**: this is about the scope of the fingerprint regarding this TLSA record.
121 | 0: fingerprint with regard to the full certificate (not recommended / [to be avoided](http://dnssec-stats.ant.isi.edu/~viktor/x3hosts.html))
122 | 1: fingerprint with regard to the public key (recommended)
123 |
124 | **Matching-Type**: information about the hashing mechanism used for fingeeprint regarding this TLSA record.
125 | 0: no hasing, full information (not recommended / [to be avoided](https://tools.ietf.org/html/rfc7672#section-3.1.2))
126 | 1: SHA2-256 hash ([recommended](https://tools.ietf.org/html/rfc7672#section-3.1.1))
127 | 2: SHA2-512 hash (not recommended / [less supported](https://www.rfc-editor.org/rfc/rfc6698.html#section-6))
128 |
129 | # Advantages of DANE explained by illustrations
130 | ## Mail delivery: TLS without DANE
131 | The illustration below shows two TLS capable mail servers without using DANE. This scenario exposes the mail transport to the risks described above.
132 |
133 | 
134 |
135 | ## Mail delivery: TLS with MITM stripping TLS
136 | The illustration below shows what happens when an attacker performs a man in the middle (MITM) attack and forces an unsecure connection by stripping the TLS capability from the receiving e-mail server.
137 |
138 | * When an attacker controls the network communication between the sending and receiving server it may downgrade the session by removing the information that indicates the receiving server supports encrypted transport. The sending server would proceed and transport the message unencrypted making all data contained in the message visible to the attacker.
139 |
140 | 
141 |
142 | ## Mail delivery: TLS with MITM using evil certificate
143 | The illustration below shows what happens when an attacker performs a man in the middle (MITM) attack and inserts its own certificate into the connection process.
144 |
145 | * Traditional SMTP protocol design does not offer any means for a sending server to automatically verify it communicates with the right server. As a result an attacker may fool the sending server to hand over the message to a server under the attackers control. This is called a man-in-the-middle attack, because the attacker places the bad server between the sending and the good receiving server.
146 | * To accieve this the attacker would configure the bad server to identify as the good receiving server. It would even use a TLS certificate that identifies with the name of the good server. The attacker would then poison the sending servers DNS telling it to go to the bad server whenever it wants to send messages to the good one. The fooled server would hand over all messages to the man-in-the-middle server.
147 |
148 | 
149 |
150 | ## Mail delivery: TLS with DANE
151 | The illustration below shows how the use of DANE can protect against man in the middle (MITM) attacks by addressing the shortcomings of TLS without DANE.
152 |
153 | * The operator of the receiving server publishes a TLSA record in its DNSSEC signed zone. The mere existence of the record indicates to the sending server that the recieving server **must** offer STARTTLS. Otherwise transport must not take place.
154 | * The receiving server's TLSA record contains policy information and a string that identifies the correct certificate. Only the good servers certiciate will match these verification criteria. Unless the attacker is also in possesion of the good servers certificate it cannot disguise as good and the man-in-the-middle server will be detected. It the verification data does not match the servers certificate the sending server will not transport the message.
155 |
156 | 
157 |
158 | ## Mail delivery: TLS with DANE without DNSSEC
159 | Although guaranteeing reliable DNS resolving is actually an advantage of DNSSEC, it is still worth mentioning here. Notice that in the example above (TLS with DANE) the lack of DNSSEC would make it possible for an attacker to alter DNS responses (2 and 4). Such an attack can be used to trick the sender into sending e-mail to a rogue e-mail server.
160 |
161 | # Reliable certificate rollover
162 | A TLSA record identifies a certificate. If the certificate is exchanged for a new one also the associated TLSA record needs to be updated. Otherwise there will be a mismatch, verification will fail and DANE aware servers will not send messages to the receivers domain.
163 |
164 | Usually this creates the intent not to exchange the certificate at all. But this collides with best-practices to replace crytographic material – certificates and keys – from time to time.
165 |
166 | To satisfy both requirements establish these maintenance procedures:
167 | * DANE allows to publish multiple TLSA records. As long as one matches the receving servers certificates everything is fine. Publish **two** sets of TLSA records like this:
168 | * One TLSA record matching the certificate currently in use.
169 | * A second TLSA record matching a fallback or future certificate to be used.
170 | * Wait two rounds of **TTL** before you start using a new certicate. This allows clients to clear their DNS cache and pick up the new TLSA record.
171 | * Once the new certificate has been deployed, tested and all is well, the legacy TLSA records may be dropped. Notice that removing the legacy TLSA record too soon can cause an outage.
172 |
173 | Two ways of handling certificate rollover are known to work well, in combination with automated monitoring to ensure that the TLSA records and certificates are always current and correct.
174 |
175 | 1. **Current + next**. This roll-over scheme always publishes two TLSA records per server certificate.
176 | - One with the SHA2-256 fingerprint of the mail server's current public key (a "3 1 1" record).
177 | - And a second with the SHA2-256 fingerprint of the mail server's next public key (also a "3 1 1" record).
178 | 2. **Current + issuer**. This roll-over scheme always publishes two TLSA records per mail server certificate.
179 | - One with the SHA2-256 fingerprint of the mail server's current public key (3 1 1)
180 | - And a second with the SHA2-256 fingerprint of the public key of an issuing CA that directly or indirectly signed the server certificate (2 1 1). This need not be (and typically is not) a root CA.
181 |
182 | ## Points of attention when rolling over using "current + next"
183 | * With the "current + next" approach, the second key pair (of which the public key can be used for the **next** TLSA "3 1 1" fingerprint) can be created / known in advance of obtaining the corresponding certificate for *that* key. In particular, if keys are rotated often enough (every 30 to 90 days or so), the next key can be pre-generated as soon as the previous key and certificate are deployed. This allows plenty of time to publish the corresponding **next** "3 1 1" TLSA record to replace the legacy record for the decommissioned key. The process begins again with another "next" key generated right away.
184 | * Deployment of a new certificate and key must be predicated (automated check) on the corresponding TLSA "3 1 1" record being in place for some time, not only on the primary DNS nameserver, but also on all secondary nameservers. Explicit queries against all the servers are to check for this are highly recommended.
185 | * Some servers have keys and certificates for multiple public key algorithms (e.g. both RSA and ECDSA). In that case, not all clients will negotiate the same algorithm and see the same key. This means that a single "3 1 1" record cannot match the server's currently deployed certificate chains. Consequently, for such servers the "3 1 1" current + "3 1 1" next TLSA records need to be provisioned separately for each algorithm. Failure to do that can result in hard to debug connectivity problems with some sending systems and not others.
186 | * Use of the same key (and perhaps wildcard certificate) across all of a domain's SMTP servers (all MX hosts) is **not** recommended. Such keys and certificates tend to be rotated across all the servers at the same time, and any deployment mistakes then lead to an outage of inbound email. Large sites with proper monitoring and carefully designed and automated rollover processes can make wildcard certificates work, but if in doubt, don't overestimate your team's ability to execute this flawlessly.
187 | * When monitoring your systems, test every IPv4 and IPv6 address of each MX host, not all clients will be able connect to every address, and none should encounter incorrect TLSA records, neglected certificates, or even non-working STARTTLS. Also test each public key algorithm, or stick to just one. All DANE-capable SMTP servers support both RSA and ECDSA P-256, so you can pick just RSA (2048-bit key) or ECDSA (P-256).
188 | * Make sure that your servers support TLS 1.2, and offer STARTTLS to all clients, even those that have not sent you email in the past. Denying STARTTLS to clients with no IP "reputation" would lock them out indefinitely if they support DANE, since they then can never send any initial mail in the clear to establish their reputation.
189 |
190 | # Tips, tricks and notices for implementation
191 | This section describes several pionts for attention when implementing DANE for SMTP.
192 |
193 | * DANE is meant to be used for the MX domain. So if you are using another domain's mail servers, make sure to ask the administrator of that domain (your mail provider) to support DANE by setting up a TLSA record.
194 | * When implementing DANE we advise to first publish DANE records on your MX domains, and then enable DANE verification on your sending mail servers.
195 | * DANE is backwards compatible. So if your mail server supports DANE and a connecting mail server does not support it yet, usually STARTTLS or plain text is used.
196 | * DANE relies on the security that is provided by DNSSEC. Make your primary domain and MX domain support DNSSEC before implementing DANE. It is important that DNSSEC is implemented properly. A lot of DANE breakage stems from receiving/recipient domains with broken DNSSEC implementations.
197 | * Purchasing of expensive certificates for mail servers has no to little added value for the confidentiality since mail servers don't validate certificates by default. Depending on the context there can be other advantages which makes organizations decide to use specific certificates.
198 | * It is possible to use self-signed certificates.
199 | * [Section 3.2 of RFC 7672](https://tools.ietf.org/html/rfc7672#section-3.2) states that SMTP clients **must not** perform certificate name checks when using an end-entity certificate (usage type 3). However, it also states that SMTP clients **must** perform certificate name checks when using an intermediate or root certificate (usage type 2). The latter is necessary to prevent attackers from abusing a random intermediate or root certificate to falsely validate their evil mail servers.
200 | * [Section 3.1 of RFC 7672](https://tools.ietf.org/html/rfc7672#section-3.1) states that the expiration date of the end-entity certificate MUST be ignored.
201 | * It is highly recommended to use a certificates public key for generating a TLSA signature (selector type "1") instead of the full certificate (selector type "0"), because this enables the reuse of key materials. Although it is wise to refresh your key material once in a while, note that the use of Forward Secrecy decreases the need to use a new key-pair on every occasion.
202 | * See also this [example of Let's Encrypt](https://mail.sys4.de/pipermail/dane-users/2019-December/000545.html). In this case the issuer certificate was changed, but the public key was not. As a result a "2 1 1" roll-over record would still validate, but a "2 0 1" roll-over record would not and requires an update.
203 | * An issuer certificate (usage type "2") validates only when the full certificate chain is offered by the receiving mail server.
204 | * Mail servers by default don't validate certificates and therefore don't have their own certificate store. That's why DANE for SMTP only supports usage type "2" (DANE-TA) and usage type "3" (DANE-EE). Usage type "0" (PKIX-TA) and usage type "1" (PKIX-EE) are not supported.
205 | * Make sure the TTL (time-to-live) of your TLSA records is not too high. This makes it possible to apply changes relatively fast in case of problems. A TTL between 30 minutes (1800) and 1 hour (3600) is recommended.
206 | * The refresh value of your full DNS zone should be in accordance with the TTL setting of your TLSA record, to make sure all name servers give the same information when (after expiration of the TLSA TTL) being queried.
207 | * In case of roll-over scheme "current + issuer", the use of the root certificate is preferred because in some contexts ([PKIoverheid](https://en.wikipedia.org/wiki/PKIoverheid)) this makes it easier to switch supplier / certificate without impacting DANE. (Remember [DigiNotar](https://en.wikipedia.org/wiki/DigiNotar)).
208 | * Roll-over scheme "current + next" gives less flexibility but the highest form of certainty, because of "tight pinning".
209 | * Implement monitoring of your DANE records to be able to detect problems as soon as possible.
210 | * Make sure your implementation supports the usage of a CNAME in your MX record. There are some inconsistencies between multiple RFC's. According to [RFC 2181](https://tools.ietf.org/html/rfc2181#section-10.3) a CNAME in MX records is not allowed, while [RFC 7671](https://tools.ietf.org/html/rfc7671#section-7) and [RFC 5321](https://tools.ietf.org/html/rfc5321#section-5.1) imply that the usage of a CNAME in MX records is allowed.
211 | * Using Server Name Indication (SNI) in an e-mail environment (for matching the certificate offered by a recieving e-mail server) is only usefull when DANE and DNSSEC are used. DNSSEC to perform a reliable MX lookup and DANE to verify the authenticity of the certificate. Sending e-mail servers (the TLS client) usually don't use SNI, because some receiving e-mail servers (the TLS server) cannot handle this; in some cases the setting up of a TLS connection fails. For more information see [RFC 7672 section 8.1](https://tools.ietf.org/html/rfc7672#section-8.1) and [this blogpost by Filippo Valsorda](https://blog.filippo.io/the-sad-state-of-smtp-encryption/).
212 | * Make sure you keep an eye on the logs of your sending mail server to see what domains fail DANE verification.
213 | * Some software allows for a test mode. This means that DANE verification is done and logged but there’s no consequence for delivery if DANE verification fails.
214 | * Manually verify a mail server's certificate with the following commands:
215 | * get the DANE record: `dig tlsa _25._tcp.mail.example.nl`
216 | * verify certificate against TLSA record `openssl s_client -starttls smtp -connect mail.example.nl:25 -dane_tlsa_domain "mail.example.nl" -dane_tlsa_rrdata "3 1 1 29c8601cb562d00aa7190003b5c17e61a93dcbed3f61fd2f86bd35fbb461d084"`.
217 | * Check if DANE TLSA records (_25._tcp.mail.example.nl) are properly DNSSEC signed. A regularly occuring mistake is the presence of "proof of non-existence" (NSEC3) for the ancestor domain (_tcp.mail.example.nl). If this happens then resolvers that use qname minimization (like the resolver used by [Internet.nl](https://internet.nl)) think that _25._tcp.mail.example.nl does not exists since _tcp.mail.example.nl does not exists. Therefore the resolver can't get the TLSA record which makes DANE fail.
218 | * Check your DNSSEC implementation on [DNSViz](https://dnsviz.net/). Enter "_25._tcp.mail.example.nl".
219 | * You can also manually check for this error. `dig _25._tcp.mail.example.nl tlsa +dnssec` results in a NOERROR response, while `dig _tcp.mail.example.nl tlsa +dnssec` results in a NXDOMAIN response.
220 |
221 | # Inbound e-mail traffic (publishing DANE DNS records)
222 | This part of the how-to describes the steps that should be taken with regard to your inbound e-mail traffic, which primairily involves publishing DANE DNS records. This enables other parties to use DANE for validating the certificates offered by your e-mail servers.
223 |
224 | ## Generating DANE records
225 | **Primary mail server (mail1.example.nl)**
226 |
227 | Generate the DANE SHA-256 hash with the following command:
228 |
229 | `openssl x509 -in /path/to/primary-mailserver.crt -noout -pubkey | openssl pkey -pubin -outform DER | openssl sha256`
230 |
231 | This command results in the following output:
232 |
233 | > (stdin)= 29c8601cb562d00aa7190003b5c17e61a93dcbed3f61fd2f86bd35fbb461d084
234 |
235 | **Secondary mail server (mail2.example.nl)**
236 |
237 | For the secondary mail server we generate the DANE SHA-256 hash using the command:
238 |
239 | `openssl x509 -in /path/to/secondary-mailserver.crt -noout -pubkey | openssl pkey -pubin -outform DER | openssl sha256`
240 |
241 | This command results in the following output:
242 | > (stdin)= 22c635348256dc53a2ba6efe56abfbe2f0ae70be2238a53472fef5064d9cf437
243 |
244 | ## Publishing DANE records
245 | Now that we have the SHA-256 hashes, we can construct the DNS records. We make the following configuration choices:
246 | * Usage field is "**3**"; we generated a DANE hash of the leaf certificate itself (DANE-EE: Domain Issued Certificate).
247 | * Selector field is "**1**"; we used the certificates' public key to generate DANE hash/signature.
248 | * Matching-type field is "**1**"; we use SHA-256.
249 |
250 | With this information we can create the DNS record for DANE:
251 |
252 | > _25._tcp.mail.example.nl. IN TLSA 3 1 1 29c8601cb562d00aa7190003b5c17e61a93dcbed3f61fd2f86bd35fbb461d084
253 | > _25._tcp.mail2.example.nl. IN TLSA 3 1 1 22c635348256dc53a2ba6efe56abfbe2f0ae70be2238a53472fef5064d9cf437
254 |
255 | ## Generating DANE roll-over records
256 | We use the provided bundle file for generating the DANE hashes belonging to the root certificate. In order to do that, we first split the bundle file into multiple certificates using `cat ca-bundle-file.crt | awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "bundlecert." c ".crt"}'`. In this specific case this results in two files: _bundlecert.1.crt_ and _bundlecert.2.crt_.
257 |
258 | For each file we view the **subject** and the **issuer**. We start with the first file using the following command:
259 |
260 | `openssl x509 -in bundlecert.1.crt -noout -subject -issuer`
261 |
262 | This results in the following output:
263 |
264 | > subject=C = GB, ST = Greater Manchester, L = Salford, O = Sectigo Limited, CN = Sectigo RSA Domain Validation Secure Server CA
265 | > issuer=C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
266 |
267 | For the second file we use the command:
268 |
269 | `openssl x509 -in bundlecert.2.crt -noout -subject -issuer`
270 |
271 | This results in the following output:
272 | > subject=C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
273 | > issuer=C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority
274 |
275 | Based on the information of these two certificates, we can conclude that the second certificate (bundlecert.2.crt) is the root certificate; since root certificates are self-signed the **subject** and the **issuer** are the same. The other certificate (bundlecert.1.crt) is an intermediate certificate which is (in this case) signed by the root certificate.
276 |
277 | ## Publishing DANE roll-over records
278 | Because we prefer the root certificate to be our roll-over anchor, we generate the DANE SHA-256 hash using the command:
279 |
280 | `openssl x509 -in bundlecert.2.crt -noout -pubkey | openssl pkey -pubin -outform DER | openssl sha256`
281 |
282 | This results in the following output:
283 |
284 | > (stdin)= c784333d20bcd742b9fdc3236f4e509b8937070e73067e254dd3bf9c45bf4dde
285 |
286 | Since both certificates for the primary and secondary come from the same Certificate Authority, they both have the same root certificate. So we don't have to repeat this with a different bundle file.
287 |
288 | Now that we have the SHA-256 hash, we can construct the DANE roll-over DNS records. We make the following configuration choices:
289 | * Usage field is "**2**"; we generated a DANE hash of the root certificate which is in the chain the chain of trust of the actual leaf certificate (DANE-TA: Trust Anchor Assertion).
290 | * Selector field is "**1**"; because we use the root certificate's public key to generate a DANE hash.
291 | * Matching-type field is "**1**"; because we use SHA-256.
292 |
293 | With this information we can create a rollover DNS record for DANE:
294 | > _25._tcp.mail.example.nl. IN TLSA 2 1 1 c784333d20bcd742b9fdc3236f4e509b8937070e73067e254dd3bf9c45bf4dde
295 | > _25._tcp.mail2.example.nl. IN TLSA 2 1 1 c784333d20bcd742b9fdc3236f4e509b8937070e73067e254dd3bf9c45bf4dde
296 |
297 | # Implementing DANE for SMTP on Postfix (inbound & outbound e-mail traffic)
298 |
299 | **Specifics for this setup**
300 | * Linux Debian 9.8 (Stretch)
301 | * SpamAssassin version 3.4.2 (running on Perl version 5.28.1)
302 | * Postfix 3.4.5
303 | * BIND 9.10.3-P4-Debian
304 | * Two certificates (for two mail servers) from Comodo / Sectigo
305 |
306 | **Assumptions**
307 | * DNSSEC is used
308 | * Mail server is operational
309 | * Software packages are already installed
310 |
311 | ## Configuring Postfix
312 | Postfix plays an important role in using DANE for validating the when available.
313 |
314 | Make sure the following entries are present in **/etc/postfix/main.cf**
315 |
316 | `smtp_dns_support_level = dnssec`
317 |
318 | This setting tells Postfix to perform DNS lookups using DNSSEC. This is an important prerequisite for DANE to be effective, since regular DNS lookups can be manipulated. Without DNSSEC support, Postfix cannot use DANE.
319 |
320 | `smtp_tls_security_level = dane`
321 |
322 | By default Postfix uses opportunistic TLS (smtp_tls_security_level = may) which is susceptible to man in the middle attacks. You could tell Postfix to use mandatory TLS (smtp_tls_security_level = encrypt) but this breaks backwards compatibility with mail servers that don't support TLS (and only work with plaintext delivery). However, when Postfix is configured to use the "dane" security level (smtp_tls_security_level = dane) it becomes resistant to man in the middle attacks, since Postfix will connect to other mail servers using "mandatory TLS" when TLSA records are found. This means that:
323 | * If TLSA records are found but are unusable, Postfix will NOT fallback to opportunistic TLS (STARTTLS) or ultimately plaintext delivery.
324 | * If no TLSA records are found (and thus DANE is not supported), Postfix will fallback to opportunistic TLS (STARTTLS) or ultimality plaintext delivery.
325 |
326 | To enforce DANE you can use "smtp_tls_security_level = dane-only" (aka mandatory DANE). With this setting a sending mail server makes sure there is no fallback to opportunistic TLS (STARTTLS) or plaintext when either the TLSA records are unusable or absent. It is possible to use mandatory DANE on a per domain basis. For example when you know a certain domain should support DANE and you want to be absolutely sure that a secure and validated delivery is used and want to prevent fallbacks to a less secure delivery method.
327 |
328 | `smtp_host_lookup = dns`
329 |
330 | This tells Postfix to perform lookups using DNS. Although this is default behavior it is important to make sure this is configured, since DANE won't be enabled if lookups are performed using a different mechanism.
331 |
332 | `smtpd_tls_CAfile = /path/to/ca-bundle-file.crt`
333 |
334 | When applying a DANE roll-over scheme using an "issuer certificate" (an intermediate or root certificate), Postfix must be able to provide the certificates of the used issuer in the chain of trust. Hence this setting.
335 |
336 | # Implementing DANE for SMTP on Exim (inbound & outbound e-mail traffic)
337 | **Specifics for this setup**
338 | * Ubuntu 18.10 ‘Cosmic Cuttlefish’
339 | * Exim 4.92 (DANE support is non-experimental since version 4.91)
340 |
341 | **Assumptions**
342 | * DNSSEC is used
343 | * Mail server is operational
344 |
345 | ## Configuration for inbound e-mail traffic
346 |
347 | ### Install or generate key pair
348 | You can use a commercial or Let's Encrypt certificate, but you can also generate your own key pair by using the provided Exim tools. Use the following command to generate a key pair.
349 |
350 | `sudo bash /usr/share/doc/exim4-base/examples/exim-gencert`
351 |
352 | ### Configure TLS
353 | In Exim you should configure TLS by adding the following to **main/03_exim4-config_tlsoptions**
354 |
355 | MAIN_TLS_ENABLE = yes
356 | tls_advertise_hosts = *
357 | tls_certificate = /path/to/certificate.crt
358 | tls_privatekey = /path/to/private.key
359 |
360 | ## Configuration for outbound e-mail traffic
361 | This part of the how-to describes the steps that should be taken with regard to your outbound e-mail traffic. This enables your e-mail environment to use DANE for validating the certificates offered by other e-mail servers.
362 |
363 | ### DNSSEC validating resolvers
364 | Make sure to configure DNSSEC validating resolvers on the mail server. When using the locale systemd resolver, make sure to add the following to **/etc/systemd/resolved.conf**.
365 |
366 | `DNSSEC = yes`
367 |
368 | ### Configure DNSSEC validation in Exim
369 | In Exim you explicitly need to configure DNSSEC validation by adding the following to **main/02_exim4-config_options** since some resolvers only validate DNSSEC on request.
370 |
371 | `dns_dnssec_ok = 1`
372 |
373 | ### Configure DANE
374 | In order to use DANE, you should tell Exim to check for DANE records when sending e-mail. You can configure DANE validation to be mandatory by adding the following to **transport/30_exim4-config_remote_smtp**.
375 |
376 | `hosts_require_dane = *`
377 |
378 | This means that TLS connections are not accepted when the domain you are trying to send mail to does not have a valid TLSA record. Since this is rather strict and not recommended to be the default, you are probably better of by configuring DANE validation to be additional. This can be done by adding the following to **transport/30_exim4-config_remote_smtp**.
379 |
380 | `hosts_try_dane = *`
381 |
382 | Notice that depending on the way you configured Exim, you need to apply DANE for all [SMTP transports](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-how_exim_receives_and_delivers_mail.html#SECTprocaddress).
383 |
384 | # Implementing DANE for SMTP on Halon (inbound & outbound e-mail traffic)
385 | Several Dutch hosting providers use Halon (a scriptable SMTP server who's virtual appliances are based on FreeBSD) as the internet facing e-mail server. The actual mail boxes reside on Direct Admin (which uses Exim) within the internal network. In this specific setup you could say that all security features are applied at the internet facing mail server which is Halon.
386 |
387 | Halon has built-in support for DANE and can be configured in several ways, this how-to uses the built-in web interface. For more information on configuration you can visit [https://halon.io/dane](https://halon.io/dane) and [https://wiki.halon.io/DANE](https://wiki.halon.io/DANE).
388 |
389 | **Specifics for this setup**
390 | * HALON 5.1-p3-argy
391 |
392 | **Assumptions**
393 | * Basic configuration of Halon
394 |
395 | ## Generic configuration
396 | ### Install or generate key pair
397 |
398 | Navigate to: `Configuration -> Email engine - > Certificates and keys -> Add`
399 |
400 | - Give an ID (name) for the certificate: only lowercase, numbers and letters (limitation of Halon).
401 | - Select type “X.509 and private key” and hit *Generate* and fill in the fields or upload a certificate (chain with private key).
402 | - Add a comment like name of the CN and date of validity.
403 |
404 | ### DNSSEC
405 | If you have a local (in your own network) DNSSEC resolver available, you can use that one. Otherwise use the internal resolver of Halon, which is based on unbound.
406 |
407 | Navigate to: `Hosts -> Network -> DNS`
408 | Name servers: enter here your resolving nameservers.
409 | DNS cache: Enable (now the DNSSEC option is visible)
410 | DNSSEC: Enable
411 |
412 | 
413 |
414 | If you have multiple hosts in a cluster, edit the DNS settings for all the hosts.
415 |
416 | ## Inbound
417 | Navigate to: `Configuration -> Email engine -> Settings`
418 |
419 | If you already have an inbound SMTP listener configured, you should upgrade this one to DANE. You can also create a new listener by navigating to `Configuration -> Server -> SMTP listeners -> Add`
420 |
421 | - STARTTLS: Enable
422 | - Certificate: Select the certificate you want to use from the dropdown menu.
423 |
424 | 
425 |
426 | ## Outbound
427 | There a multiple ways to enable outbound DANE: through a Transport Label or scripting.
428 |
429 | ### Transport Label
430 | Navigate to: `Configuration -> Email Engine -> Transport labels`
431 |
432 | Select the transport labels ID if you want to upgrade an existing one, otherwise click *Add* to create a new one.
433 |
434 | - Destination hostname and port: Select Deliver to MX.
435 | - TLS STARTTLS: Select "DANE" or "DANE (required)" in the dropdown menu.
436 |
437 | 
438 |
439 | Notice: with "DANE (required)" there is no fallback to TLS or none option.
440 |
441 | If you created a new "Transport Label" use that one in a Script mapping as Transport to use the DANE enabled delivery.
442 |
443 | ### Scripting
444 | With the SetTLS function in pre-delivery you can set the TLS options for that specific delivery attempt.
445 |
446 | `SetTLS(["tls" => "dane”]);`
447 |
448 | `SetTLS(["tls" => "dane_require”]);`
449 |
450 | ### Logging
451 |
452 | In the logging you can see whether delivery via DANE is used.
453 |
454 | Example logging:
455 | ```
456 | Processing message from to with daneenabled
457 | Delivering message to example.nl:25
458 | Connecting to [192.168.1.1]:25 (DNSSEC)
459 | Connection is now using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256 bits) (DANE)
460 | Successful delivery to (retry 0) in 2.108s: 2.0.0 example.org accepted message 0123456789ABCD
461 | Deleting mail file (all recipients done)
462 | ```
463 |
464 | # Implementing DANE for SMTP on Port25 PowerMTA (outbound e-mail traffic)
465 | Various Email Service Providers (ESP) are using Port25 PowerMTA as their preferred outbound MTA.
466 |
467 | Port25 has build-in support for DANE since version v5.0
468 |
469 | **Specifics for this setup**
470 | * Port25 PowerMTA v5.x+
471 |
472 | **Assumptions**
473 | * Basic configuration of PowerMTA
474 | * TLS Certificate already in place
475 | * STARTTLS active (`use-starttls yes`)
476 | * DNSSEC capable DNS resolver
477 |
478 | ## Generic configuration
479 | ### Configure the use of DANE on the domain level
480 |
481 | Within the configuration of a specific domain
482 | ```
483 |
484 | use-dane true
485 |
486 | ```
487 |
488 | Within the configuration of all domains
489 | ```
490 |
491 | use-dane true
492 |
493 | ```
494 |
495 | If TLS is required per DANE for SMTP `use-starttls` and `require-starttls` will be overridden.
496 |
497 | ### Logging
498 |
499 | By enabling the resolver logging (`log-resolution yes`) you are able to see the DNS queries for the TLSA records.
500 |
501 | Within the configuration of a specific domain
502 | ```
503 |
504 | log-resolution yes
505 |
506 | ```
507 |
508 | Within the configuration of all domains
509 | ```
510 |
511 | log-resolution yes
512 |
513 | ```
514 |
515 | By enabling the TLS logging (`log-tls yes`) you are able to see the TLS DANE verification.
516 |
517 | Within the configuration of a specific domain
518 | ```
519 |
520 | log-tls yes
521 |
522 | ```
523 |
524 | Within the configuration of all domains
525 | ```
526 |
527 | log-tls yes
528 |
529 | ```
530 |
531 | Example logging:
532 |
533 | DNS Resolver
534 | ```
535 | Starting new query for TLSA _25._tcp.jail.internet.nl:
536 | answer _25._tcp.jail.internet.nl. 3600 IN CNAME 3.1.1._dane.internet.nl.
537 | answer _25._tcp.jail.internet.nl. 3600 IN CNAME 3.1.1._dane.internet.nl.
538 | answer 3.1.1._dane.internet.nl. 3600 IN TLSA 3 1 1 25DE2127E359B8522DDD6E237381458804549CDE5440E4F5B547C5629E48D46B
539 | _25._tcp.jail.internet.nl. 3600 IN CNAME 3.1.1._dane.internet.nl.
540 | _25._tcp.jail.internet.nl. 3600 IN CNAME 3.1.1._dane.internet.nl.
541 | 3.1.1._dane.internet.nl. 3600 IN TLSA 3 1 1 25DE2127E359B8522DDD6E237381458804549CDE5440E4F5B547C5629E48D46B
542 | ```
543 | TLS Logging
544 | ```
545 | tls: TLSv1.2 connected with 256-bit ECDHE-RSA-AES256-GCM-SHA384
546 | tls: cert: /C=NL/ST=Zuid-Holland/L=Den Haag/XXXXXXX; issuer=/C=NL/O=XXXXXXX; verified=yes
547 | tls: DANE match: /C=NL/ST=Zuid-Holland/L=XXXXXX; issuer=XXXXX; depth=0
548 | ```
549 |
550 | # Special thanks
551 | Our infinite gratitude goes out to the following people for their support in building this how-to for DANE.
552 |
553 | Alexander Brotman
554 | Alwin de Bruin
555 | Anders Berggren
556 | Marc van de Geijn
557 | Mark Scholten
558 | Patrick Koetter
559 | Simon Besteman
560 | Tom van Leeuwen
561 | Viktor Dukhovni
562 | Wido Potters
563 |
--------------------------------------------------------------------------------
/DKIM-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # DKIM how-to
4 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on implementing DKIM.
5 |
6 | # Table of contents
7 | - [What is DKIM?](#what-is-dkim-)
8 | - [Why use DKIM?](#why-use-dkim-)
9 | - [Tips, tricks and notices for implementation](#tips--tricks-and-notices-for-implementation)
10 | * [Canonicalization](#canonicalization)
11 | - [Implementing DKIM with OpenDKIM for Postfix with SpamAssassin](#implementing-dkim-with-opendkim-for-postfix-with-spamassassin)
12 | * [Outbound e-mail traffic](#outbound-e-mail-traffic)
13 | + [Set up OpenDKIM and created key pair for your domain](#set-up-opendkim-and-created-key-pair-for-your-domain)
14 | - [Key rotation](#key-rotation)
15 | + [Publish the DNS record](#publish-the-dns-record)
16 | + [Configure Postfix](#configure-postfix)
17 | * [Inbound e-mail](#inbound-e-mail)
18 | + [Configuring SpamAssassin](#configuring-spamassassin)
19 | - [Implementing DKIM on HALON](#implementing-dkim-on-halon)
20 | * [Generic configuration](#generic-configuration)
21 | + [Upload or generate private key](#upload-or-generate-private-key)
22 | * [Outbound signing](#outbound-signing)
23 | * [Inbound email](#inbound-email)
24 | - [Special thanks](#special-thanks)
25 |
26 | Table of contents generated with markdown-toc
27 |
28 | # What is DKIM?
29 | DKIM stands for **D**omain**K**eys **I**dentified **M**ail and is described in [RFC 6376](https://tools.ietf.org/html/rfc6376) with updates in [RFC 8301](https://tools.ietf.org/html/rfc8301) and [RFC 8463](https://tools.ietf.org/html/rfc8463). It is meant to provide the owner of a domain with the means to claim that a message has actually been send by the domain's e-mail server and should therefore be considered legitimate. It works by signing every individual e-mail message with a specific key (private key), so that the receiving party can use a corresponding key (public key) published in the sending domain's DNS record to validate the e-mail authenticity and to check whether the e-mail has not been tampered with.
30 |
31 | # Why use DKIM?
32 | A common used technique used by spammers is to trick the receiving party into believing an e-mail is legitimate by using a forged sender address. This is also known as e-mail spoofing. DKIM has been designed to protect against spoofing. If an incoming e-mail does not have a DKIM signature or when it's DKIM signature does not validate, the receiving e-mail server should consider the e-mail to be SPAM.
33 |
34 | # Tips, tricks and notices for implementation
35 | * Use a DKIM key (RSA) of [at least 1024 bits](https://tools.ietf.org/html/rfc6376#section-3.3.3) to minimize the successrate of offline attacks. Don't go beyond a key size of 2048 bits since this is not mandatory according to the RFC.
36 | * Make sure to change your DKIM keys regularly. A rotation scheme of 6 months is recommended.
37 | * If a domain is not using e-mail (anymore), it is recommended to set an empty public key: "v=DKIM1; p=".
38 | * When used with a specific selector, an empty public key means that e-mail signed with the associated public key must be considered unreliable since they public key was revoked.
39 | * When used with a wildcard selector, setting an empty public key indicates that all previously used keys are revoked and must be considered unreliable. The owner of a domain can also use this to explicitly signal that a domain is not configured to use e-mail.
40 | * [According to the RFC](https://tools.ietf.org/html/rfc6376#section-6.1.2) the absence of a selector / public key (e.g. as a result of deleting the entire DKIM resource record) is semantically equal to a resource record with an empty public key. This means that both approaches should be treated similar by the receiving mail server.
41 |
42 | ## Canonicalization
43 | As mentioned in [RFC 6376 section 3.4](https://tools.ietf.org/html/rfc6376#section-3.4) some mail systems modify e-mail in transit. This type of modification is called canonicalization and is generally used to make things comparable before presenting the email to the signing or verification algorithm. You can imagine that this is important when signing and validating an e-mail; if things change too much this can invalidate a DKIM signature, which also impacts DMARC.
44 |
45 | DKIM software allows you to specify the canonicalization settings. The settings used by the sender are set in the DKIM header of every e-mail using the "c" tag. Accepted values are "relaxed" and "simple" and since canonicalization exists for both the header and the body of an e-mail, the format used to represent the canonicalization setting is "value/value" for header and body respectively.
46 |
47 | We currently advise against the "simple/simple" canonicalization setting because this (being the most strict setting) tolerates almost no modification of the header and body before signing, which is prone to cause problems when forwarding mail. This is confirmed in RFC 7960 [section 2.3](https://tools.ietf.org/html/rfc7960#section-2.3) and [section 4.1.1.2](https://tools.ietf.org/html/rfc7960#section-4.1.1.2). Therefore we recommend to use the "relaxed/relaxed" setting which tolerates common modifications of the header and body before signing.
48 |
49 | Notice:
50 | * When not specified DKIM canoncalization defaults to "strict/strict".
51 | * "c=strict" equals "c=strict/strict", but "c=relaxed" equals "relaxed/strict".
52 |
53 | # Implementing DKIM with OpenDKIM for Postfix with SpamAssassin
54 | **Specifics for this setup**
55 | * Linux Debian 9.8 (Stretch)
56 | * SpamAssassin version 3.4.2 (running on Perl version 5.28.1)
57 | * Postfix 3.4.5
58 | * BIND 9.10.3-P4-Debian
59 | * OpenDKIM v2.11.0
60 |
61 | **Assumptions**
62 | * DNSSEC is used
63 | * Mail server is operational
64 | * Software packages are already installed
65 |
66 | ## Outbound e-mail traffic
67 | DKIM for outbound e-mail traffic can be accomplished by publishing a DKIM policy as a TXT record in a domain name's DNS zone, and by configuring the e-mail server to sign outbound e-mails.
68 |
69 | ### Set up OpenDKIM and created key pair for your domain
70 | Make sure the file ***/etc/opendkim.conf** has a least the following configuration options.
71 |
72 | UMask 002
73 | Canonicalization relaxed/relaxed
74 | Mode sv
75 | AutoRestart Yes
76 | AutoRestartRate 10/1h
77 | ExternalIgnoreList refile:/etc/opendkim/trusted_hosts
78 | InternalHosts refile:/etc/opendkim/trusted_hosts
79 | KeyTable refile:/etc/opendkim/key_table
80 | SigningTable refile:/etc/opendkim/signing_table
81 | PidFile /var/run/opendkim/opendkim.pid
82 | SignatureAlgorithm rsa-sha256
83 | UserID opendkim:opendkim
84 | Socket inet:12301@localhost
85 |
86 | Create the the file **/etc/opendkim/trusted_hosts** and make sure it contains the following:
87 |
88 | 127.0.0.1
89 | localhost
90 | example.nl
91 | mail.example.nl
92 |
93 | Now create the directory **/etc/opendkim/keys/example.nl** and execute the following command with this directory and make sure to replace 'YYYYMM' with the number of the current year and month. For example: "selector201906". This makes it easier to determine the age of a specific key in a later stage.
94 |
95 | `opendkim-genkey -s selectorYYYYMM -d example.nl`
96 |
97 | There are now 2 files in **/etc/opendkim/keys/example.nl** (the key pair):
98 | * selector201906.txt: this file contains DNS complete DKIM DNS record including the public key.
99 | > selector201906._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCooJQftNOg3wOqVW5wOpr1PhhzgeP1IE9dTOtpUOCENP+z1HwP+8fFp9aGo/EKHoDQRhDUxXlVfocmRjb0lyjHD5ax16BBKLAd8+AgHZt1er8fmm2cL+7nurv0vU5YBG9LGUklD9qO/zJrIz+Lp+YO7D2rt0qYAgGzUOLJBWLBNQIDAQAB" ; ----- DKIM key selector201906 for example.nl
100 |
101 | * selector201906.private: this file contains the private key which is going to be used by Postfix to sign all outbound e-mails.
102 |
103 | > -----BEGIN RSA PRIVATE KEY-----
104 | > MIICXAIBAAKBgQCooJQftNOg3wOqVW5wOpr1PhhzgeP1IE9dTOtpUOCENP+z1HwP
105 | > +8fFp9aGo/EKHoDQRhDUxXlVfocmRjb0lyjHD5ax16BBKLAd8+AgHZt1er8fmm2c
106 | > L+7nurv0vU5YBG9LGUklD9qO/zJrIz+Lp+YO7D2rt0qYAgGzUOLJBWLBNQIDAQAB
107 | > AoGASy+V+/Efbxogw0DmRgoLb4+pTU87+d7XJC2YxVN3V9tdq6vxSRslPr8QCuZs
108 | > Ievp2XN0K7qE2BbbYbhq5nHDjwzPJ7vCZzN3JI8eOC9gKP++Te6AAcDjP+G3LND4
109 | > Np2AWsn6JwGeM0QYI5Ehrxrw5HlqNb620N6wOEyd/7s4Px0CQQDVT3LhDzUOkbAW
110 | > J/jUHdV4WYozRcBGFFJvH85ASJAbK9OSrF3tfJZj9e78xP4Z5EZ8jp9iKgajt5zl
111 | > fYtAYjZfAkEAyl/gascX17nxO3rH/8hr8dPS6hY0KYTKXCZfuvYaSG7AZ3oaSQSc
112 | > mz3Rz67cm14DZNc01aBE7PwiRjq9TsQo6wJAQkppijXeqENwdMJBWzJWWAuDnoGL
113 | > ynugTraUs3eZiUgqfUeh/R8d4bzZY6aYzVUa7rSoJaqn25NBaDSG5SBggwJBAKrp
114 | > VepXwjcafjSxeP74ENHnBxVTMzJtR0mTzv1iosfRYQUDBffswSYKi4tOLlm4iD09
115 | > 0w0nkY5jUb7mFMLUv4kCQDjgGWNO8AeAohYF47fmYXeMMvS29rKtdLiR7D41WtOo
116 | > +zM7YTQa9kGRihEK+iT8v1x7ZX3mt0WZ5eoupeGauio=
117 | > -----END RSA PRIVATE KEY-----
118 |
119 | Now make sure that the private key can only be read by the user opendkim by executing the following command:
120 |
121 | `chown opendkim:opendkim selector201906.private`
122 |
123 | The next step is to create the key table file **/etc/opendkim/key_table**. This file will tell opendkim about the domains that have been configured and where to find their keys. Add the following to configure example.nl:
124 |
125 | > selector201906._domainkey.example.nl example.nl:selector201906:/etc/opendkim/keys/example.nl/selector201906.private
126 |
127 | Create the file **/etc/opendkim/signing_table** and add the following line:
128 |
129 | > *@example.nl selector201906._domainkey.example.nl
130 |
131 | Start OpenDKIM and check your logfiles for possible errors.
132 |
133 | #### Key rotation
134 | OpenDKIM does not support the automated rotation of DKIM keys. This means that you should rotate your keys manually, write a script to do this, or use an existing script like [https://github.com/tetsuo13/OpenDKIM-Rotate-Keys](https://github.com/tetsuo13/OpenDKIM-Rotate-Keys) or [https://github.com/captbrando/dkimrotator](https://github.com/captbrando/dkimrotator).
135 |
136 | ### Publish the DNS record
137 |
138 | Make sure to add the following lines to you domain's zone file:
139 | > selector201906._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCooJQftNOg3wOqVW5wOpr1PhhzgeP1IE9dTOtpUOCENP+z1HwP+8fFp9aGo/EKHoDQRhDUxXlVfocmRjb0lyjHD5ax16BBKLAd8+AgHZt1er8fmm2cL+7nurv0vU5YBG9LGUklD9qO/zJrIz+Lp+YO7D2rt0qYAgGzUOLJBWLBNQIDAQAB"
140 |
141 | > _adsp._domainkey IN TXT "dkim=all"
142 |
143 | The first line publishes the selector and the associated public key. The second line tells receiving mail server that all e-mail coming from the domain example.nl are DKIM signed.
144 |
145 | ### Configure Postfix
146 | The final step is to configure Postfix to actually sign outbound e-mail using OpenDKIM. In order to do this add the following to **/etc/postfix/main.cf**:
147 |
148 | milter_protocol = 6
149 | milter_default_action = accept
150 | smtpd_milters = inet:localhost:12301
151 | non_smtpd_milters = inet:localhost:12301
152 |
153 | When you are ready to start using DKIM restart Postfix, but make sure you waited long enough for the DKIM DNS record to succesfully propagate.
154 |
155 | ## Inbound e-mail
156 |
157 | ### Configuring SpamAssassin
158 | SpamAssassin uses a scoring mechanism in order to determine if an e-mail should be considered spam. By default SpamAssassin considers an e-mail to be spam if the score at least "5". An e-mail starts with a score of 0 and points are added based on the [tests](https://spamassassin.apache.org/old/tests_3_3_x.html) performed. The tests performed can be configured by adding specific [configuration parameters](https://spamassassin.apache.org/full/3.4.x/doc/Mail_SpamAssassin_Conf.html) in **/etc/spamassassin/local.cf**.
159 |
160 | Now here's the tricky part. The points added to the score of an incoming e-mail based on the results of a specific test, is at its core a custom job. Many variables can be taken into consideration when scoring an e-mail (which is considered the strength of a post-SMTP spam filter) and the detailed scoring depends on a domain owner's specific wishes. For the sake of this how-to, the DKIM scoring will be based on the assumption that the domain owner wants to consider an e-mail to be spam if the sending e-mail server's DKIM signature is not valid.
161 |
162 | With SpamAssassin this can be configured by adding the following scoring configuration parameters to **/etc/spamassassin/local.cf**:
163 |
164 | ```
165 | score DKIM_ADSP_ALL 5.0
166 | # No valid author signature, domain signs all mail
167 |
168 | score DKIM_ADSP_DISCARD 5.0
169 | # No valid author signature, domain signs all mail and suggests discarding the rest
170 |
171 | score DKIM_ADSP_NXDOMAIN 5.0
172 | # No valid author signature and from-domain does not exist
173 | ```
174 |
175 | This means that incoming e-mail is instantly classificied as spam if there is not a valid DKIM signature in the mail header and:
176 | * the sending domain's DKIM ADSP record states that all e-mail should be signed and all unsigned mails should be discarded (DISCARD).
177 | * the sending domain's DKIM ADSP record states that all e-mail should be signed (ALL).
178 | * the domain used in the "From"-header (a.k.a. RFC5322.From, Header From, Message From) does not exist.
179 |
180 | # Implementing DKIM on HALON
181 | This example uses the internal capabilities of Halon for DKIM, it is possible to retrieve the private keys externally but that is outside this scope.
182 |
183 | **Specifics for this setup**
184 | - HALON 5.1-p3-argy
185 |
186 | **Assumptions**
187 | - Basic configuration of Halon
188 | - Operating DNS Server
189 |
190 | ## Generic configuration
191 | ### Upload or generate private key
192 |
193 | Navigate to: `Configuration -> Email engine -> Certificates and keys -> Add`
194 |
195 | - Give a ID (name) for the private key. Only lowercase, numbers and letters (limitation of Halon).
196 | - Select Type Private key and hit Generate.
197 | - Add a Comment like the selector name and date.
198 | - Click Add and then select the newly created private key in the overview, then click Details on top of the page.
199 |
200 | 
201 |
202 | On the detail page you see the public key you just generated and the button "DKIM record".
203 |
204 | 
205 |
206 | Click the "DKIM record" button and give your Domain and Selector which you want to use. Hit "Generate" and here you see the DKIM record which you can use in your DNS server.
207 |
208 | 
209 |
210 | Publish the DNS record for the domain in your DNS environment.
211 |
212 | ## Outbound signing
213 |
214 | Navigate to `Configuration -> Code editor` select there the End of Data (EOD/EOD rcpt) script where you want to use the DKIM signing.
215 |
216 | Before the `GetMailMessage()->queue()` add:
217 |
218 | ```php
219 | $dkimselector = "selector201909"; // Selector
220 | $dkimdomain = "example.nl"; // Header From:
221 | $dkimcertificate = "selector201909"; // certificate ID
222 | if (GetMailMessage()->signDKIM($dkimselector,$dkimdomain,"pki:".$dkimcertificate) == none)
223 | Defer("DKIM signing error, bla bla error message");
224 | ```
225 |
226 | The following syslog message is visible in the logging if DKIM signing is successful.
227 | ```
228 | [6xxxxxxb-dxxf-1xx9-bxx5-0xxxxxxxxx4] [EOD] DKIM signed for example.nl (selector201909) with signature b=Yxxxxx9
229 | ```
230 |
231 | ## Inbound email
232 |
233 | Navigate to `Configuration -> Code editor` select there the End of Data (EOD/EOD rcpt) script where the incoming mail is checked.
234 |
235 | Before the `GetMailMessage()->queue()` add:
236 |
237 | ```php
238 | foreach (GetMailMessage()->getHeaders("DKIM-Signature", ["field" => true]) as $i => $dkimsign) {
239 | $dkimresult = GetMailMessage()->verifyDKIM($dkimsign);
240 | if ($dkimresult["result"] not "pass") {
241 | // Do something like Defer() Reject() or higher spamscore;
242 | }
243 | }
244 | ```
245 |
246 | The following syslog message is visible in the logging
247 | ```
248 | on success:
249 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] Processing (dkimverify)
250 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] DKIM(example.nl): Successfully verified
251 |
252 | on error, selector not found in DNS:
253 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] Processing (dkimverify)
254 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] DKIM(example.nl): Permanent error: No key for signature selector201909._domainkey.example.nl
255 |
256 | on error, Mailbody has been manipulated:
257 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] Processing (dkimverify)
258 | [2xxxxxxc-dxx2-1xx9-axxv-xxxxxxxxxxxa] [EOD] DKIM(example.nl): Permanent error: Body hash did not verify
259 | ```
260 |
261 | # Special thanks
262 | Our infinite gratitude goes out to the following people for their support in building this how-to for DKIM.
263 |
264 | Tom van Leeuwen
265 |
--------------------------------------------------------------------------------
/DMARC-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # DMARC how-to
4 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on implementing DMARC.
5 |
6 | # Table of contents
7 | - [What is DMARC?](#what-is-dmarc-)
8 | - [Why use DMARC?](#why-use-dmarc-)
9 | - [Tips, tricks and notices for implementation](#tips--tricks-and-notices-for-implementation)
10 | - [Overview of DMARC configuration tags](#overview-of-dmarc-configuration-tags)
11 | - [Implementing DMARC with OpenDMARC for Postfix with SpamAssassin](#implementing-dmarc-with-opendmarc-for-postfix-with-spamassassin)
12 | * [Outbound e-mail traffic](#outbound-e-mail-traffic)
13 | + [Setting up a DMARC record](#setting-up-a-dmarc-record)
14 | * [Inbound e-mail traffic](#inbound-e-mail-traffic)
15 | + [Set up OpenDMARC](#set-up-opendmarc)
16 | + [Integrate with Postfix](#integrate-with-postfix)
17 | + [Set up reporting](#set-up-reporting)
18 | + [Configuring SpamAssassin](#configuring-spamassassin)
19 | - [Special thanks](#special-thanks)
20 |
21 | Table of contents generated with markdown-toc
22 |
23 | # What is DMARC?
24 | DMARC is short for **D**omain based **M**essage **A**uthentication, **R**eporting and **C**onformance and is described in [RFC 7489](https://tools.ietf.org/html/rfc7489). With DMARC the owner of a domain can, by means of a DNS record, publish a policy that states how to handle e-mail (deliver, quarantine, reject) which is not properly authenticated using SPF and/or DKIM. Because DMARC depends on the security of DNS, the use of DNSSEC is highly recommended.
25 |
26 | At the same time DMARC also provides the means for receiving reports which allows a domain's administrator to detect whether their domainname is used for phishing or spam.
27 |
28 | # Why use DMARC?
29 | Before DMARC, organizations already took several measures to determine the authenticity of an e-mail (like SPF and DKIM) to reduce the received amount of SPAM to a minimum. This is basically a good thing, but if these measures fail to choose whether or not an email is SPAM with a high level of certainty, the choice is redirected to the addressee (receiving party). This methodology is prone to abuse, since users are generally not equiped with the knowledge and/or means to classify incoming emails.
30 |
31 | DMARC addresses this problem and enables the owner of a domain to take explicit responsiblity with regard to the actions taken by the sending party when the validity of an incoming email cannot be determined.
32 |
33 | # Tips, tricks and notices for implementation
34 | * Interoperabily issues: https://tools.ietf.org/html/rfc7960
35 | * DMARC does not require both DKIM or SPF. But implementation of both is strongly advised.
36 | * DMARC is about aligning the domain in the DKIM header (the "d=" value) and SPF header (*a.k.a. RFC5321.MailFrom, Return-Path, Envelope Sender, Envelope From*) with the organizational domain in the message From header (*a.k.a. RFC5322.From, Header From, Message From*) which is visible to the user.
37 | * If these values do not align this could mean for example, that an attacker placed a valid DKIM signature header in an email with a "d=" value that points to a domain the attacker controls, allowing DKIM to pass while still spoofing the From address to the user.
38 | * Parked domain: “DMARC p=reject”. Make sure to include rua and ruf addresses, since this allows monitoring of possible abuse attempts. Implement additional records (SPF, DKIM, NullMX) if possible, see also our [Parked domain how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/parked-domain-how-to.md).
39 | * RFC 7489 [states](https://tools.ietf.org/html/rfc7489#section-6.4) that the tags dmarc-version ("v=") and dmarc-request ("p=") should be on the first and second position of the DMARC record. The order of the other tags does not matter: "components other than dmarc-version and dmarc-request may appear in any order".
40 | * The verified [erratum 5440 of RFC 7489](https://www.rfc-editor.org/errata_search.php?rfc=7489) states that a semicolon should be included in the DMARC version tag. Correct: "v=DMARC1;". Incorrect: "v=DMARC1".
41 | * When using office 365, the forwarding of calendar appointments from a DMARC projected domain fails. This is a known issue. Read more on the [Office 365 UserVoice forum](https://office365.uservoice.com/forums/264636-general/suggestions/34012756-forwarding-of-calendar-appointments-from-a-dmarc-p) and don't forget to submit your vote!
42 | * There is a workaround: Forward the appointment as an "iCalendar file" or as an attachment.
43 | * When processing incoming mail we advise to favor a DMARC policy over an SPF policy. Do not configure SPF rejection to go into effect early in handling, but take full advantage of the enhancements DMARC is offering. A message might still pass based on DKIM.
44 | * At the same time, be aware that some operaters still allow a hard fail (-all) to go into effect early in handling and skip DMARC operations.
45 | * When using a different domain for the rua and/or ruf address, make sure that the DMARC authorization records (example.nl._report._dmarc.differentdomain.nl) are properly DNSSEC signed. A regularly occuring mistake is the presence of "proof of non-existence" (NSEC3) for the ancestor domain (_report._dmarc.differentdomain.nl). If this happens then resolvers that use qname minimization (like the resolver used by [Internet.nl](https://internet.nl)) think that example.nl._report._dmarc.differentdomain.nl does not exists since _report._dmarc.differentdomain.nl does not exists. Therefore the resolver can't get the DMARC authorization record which makes DMARC fail.
46 | * Check your DNSSEC implementation on [DNSViz](https://dnsviz.net/). Enter "example.nl._report._dmarc.differentdomain.nl".
47 | * You can also manually check for this error. `dig example.nl._report._dmarc.differentdomain.nl +dnssec` results in a NOERROR response, while `dig _report._dmarc.differentdomain.nl +dnssec` results in a NXDOMAIN response.
48 |
49 | # Overview of DMARC configuration tags
50 | The DMARC policy is published by means of a DNS TXT record. A DMARC record can contain several configuration tags. The table below will list all configuration tags and explain their purpose.
51 |
52 | | DMARC configuration tag | Required? | Value(s) | Explanation |
53 | | --- | --- | --- | --- |
54 | | v | mandatory | DMARC1; | |
55 | | p | mandatory | none quarantine reject | None: don't do anything if DMARC verification fails (used for testing) quarantine: treat mail that fails DMARC check as suspicious reject: reject mail that fail DMARC check |
56 | | rua | optional | rua@example.nl | This field contains the email address used to send **aggregate** reports to |
57 | | ruf | optional |ruf@example.nl | This field contains the email address used to send **forensic** reports to |
58 | | fo | mandatory | 0 1 s d | Reporting options for failure reports. Generates a report if: - both SPF and DKIM tests fail (0) - either SPF or DKIM test fail (1) - SPF test fails (s) - DKIM test fails (d) |
59 | | adkim | optional | s r | Controls how strict the result of DKIM alignment checks should be intepreted. Strict or relaxed. |
60 | | aspf | optional | s r | Controls how strict the result of SPF alignment checks should be intepreted. Strict or relaxed. |
61 | | pct | optional | 0..100 | Determine percentage of mail from your domain to have the DMARC verificaton done by other mail providers. Default is 100. |
62 | | rf | optional | afrf aodef | Preferred reporting format for forensic reports. Default is afrf. |
63 | | ri | optional | 60..86400 | Interval (in seconds) at which you want to receive DMARC reports. The DMARC RFC specifies that organizations should be able to send reports at least once every day (86400 seconds) and also states a minimum interval of one hour (60 seconds). Default is 86400. |
64 | | sp | optional | none quarantine reject | Subdomain policy. If not set the policy defined under "p=" will apply to all your subdomains. |
65 |
66 | Be aware that implementing a DMARC record without a rua configuration is possible, this is not advised because the DMARC XML files that are received by implementing a rua email address can help with implementing DKIM or SPF to meet the DMARC requirements.
67 |
68 | # Implementing DMARC with OpenDMARC for Postfix with SpamAssassin
69 | **Specifics for this setup**
70 | * Linux Debian 9.8 (Stretch)
71 | * Postfix 3.4.5
72 | * BIND 9.10.3-P4-Debian
73 | * OpenDMARC v1.3.2
74 |
75 | **Assumptions**
76 | * DNSSEC is used
77 | * Mail server is operational
78 | * Software packages are already installed
79 |
80 | ## Outbound e-mail traffic
81 | DMARC for outbound e-mail traffic can be accomplished by publishing a DMARC policy as a TXT record in a domain name's DNS zone.
82 |
83 | ### Setting up a DMARC record
84 | Depending on your preferences and needs, you can determine the value of the configuration tags. The values below seem like a good starting point when setting up DMARC.
85 |
86 | _dmarc IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.nl; ruf=mailto:dmarc@example.nl; fo=1; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; sp=quarantine"
87 |
88 | Because this specific setup uses SpamAssassin for classifying e-mail to be SPAM or legitimate (HAM), the DMARC policy used is quarantine. This is done to prevent OpenDMARC from blocking the e-mail and, as a result, not enabling SpamAssassin to do its job.
89 |
90 | ## Inbound e-mail traffic
91 | Ideally incoming e-mail is processed by making a **single decision** based on a collective evaluation of all relevant e-mail standards (SPF, DKIM, DMARC). Although this would be the most elegant way of processing incoming e-mail, most e-mail servers process e-mail standards in a sequential order. This should be taken into consideration when configuring your own e-mail environment; depending on a domain owner's preferences it is also possible to implement a "single decision" e-mail environment.
92 |
93 | Thereafter, it is [stated in the DMARC RFC](https://tools.ietf.org/html/rfc7489#section-10.1) that some receiver architectures might implement SPF in advance of any DMARC operations. This means that a "-" prefix on a sender's SPF mechanism, such as "-all", could cause that rejection to go into effect early in handling, causing message rejection before any DMARC processing takes place. While operators choosing to use "-all" should be aware of this, we advise to favor a DMARC policy over an SPF policy. As also [stated in the DMARC RFC](https://tools.ietf.org/html/rfc7489#section-6.7), the final diposition of a message is always a matter of local policy. With this in mind we feel that operators should not configure SPF rejection to go into effect early in handling, and thus disregarding DMARC. At the cost of processing the entire message body, we advise to always evaluate all relevant standards before coming to a decision. If SPF fails, DKIM might still pass resulting in a satisfying DMARC evaluation.
94 |
95 | DMARC for inbound e-mail traffic can be accomplished by setting up OpenDMARC and integrate it with Postfix.
96 |
97 | ### Set up OpenDMARC
98 | Make sure the file **/etc/opendmarc.conf** has a least the following configuration parameters.
99 |
100 | AuthservID mail.example.nl
101 | PidFile /var/run/opendmarc/opendmarc.pid
102 | RejectFailures false
103 | Syslog true
104 | TrustedAuthservIDs mail.example.nl,mail2.example.nl,localhost,127.0.0.1
105 | UMask 0002
106 | UserID opendmarc:opendmarc
107 | IgnoreAuthenticatedClients true
108 | IgnoreHosts /etc/opendmarc/ignore.hosts
109 | HistoryFile /var/run/opendmarc/opendmarc.dat
110 | Socket inet:54321@localhost
111 |
112 | For more information about these configuration parameters, take a look at [its man page](https://manpages.debian.org/unstable/opendmarc/opendmarc.conf.5.en.html).
113 |
114 | Make sure the file **/etc/opendmarc/ignore.hosts** contains all hosts that you trust. The e-mail coming from these hosts will not be checked by OpenDMARC:
115 |
116 | 127.0.0.1
117 | localhost
118 |
119 | Make sure the default file **/etc/default/opendmarc** contains:
120 |
121 | RUNDIR=/var/run/opendmarc
122 | SOCKET=inet:54321@localhost
123 | USER=opendmarc
124 | GROUP=opendmarc
125 | PIDFILE=$RUNDIR/opendmarc.pid
126 |
127 | ### Integrate with Postfix
128 | Now we need to tell Postfix to use OpenDMARC as a mail filter in order to use its functionality. This is done by making sure that **/etc/postfix/main.cf** contains the configuration parameters as listed below. Notice that the DKIM check (localhost:12301) is done _before_ DMARC (localhost:54321) since DMARC relies on the DKIM results.
129 |
130 | smtpd_milters = inet:localhost:12301,inet:localhost:54321
131 | non_smtpd_milters = inet:localhost:12301,inet:localhost:54321
132 |
133 | ### Set up reporting
134 | In order to become DMARC compliant, we need to start sending aggregate DMARC reports. Let's start by creating an OpenDMARC database using the provided MySQL script (/usr/share/doc/opendmarc/schema.mysql) in the OpenDMARC package.
135 |
136 | In the example below the user root is used to create the new "opendmarc" database. If you would also like to create a user with access to the database, then uncomment the last two lines in the MySQL script and make sure to change the default 'changeme' password into something strong.
137 |
138 | mysql -u root -p < /usr/share/doc/opendmarc/schema.mysql
139 |
140 | After creation of the database, you need to create an executable Bash script in order to be able to send out reports. Create the file **/etc/opendmarc/report_script** and use the following content:
141 |
142 | ```
143 | #!/bin/bash
144 |
145 | DB_SERVER='database.example.nl'
146 | DB_USER='opendmarc'
147 | DB_PASS='somethingstrong'
148 | DB_NAME='opendmarc'
149 | WORK_DIR='/var/run/opendmarc'
150 | REPORT_EMAIL='dmarc@example.nl'
151 | REPORT_ORG='Example Company (example.nl)'
152 |
153 | mv ${WORK_DIR}/opendmarc.dat ${WORK_DIR}/opendmarc_import.dat -f
154 | cat /dev/null > ${WORK_DIR}/opendmarc.dat
155 |
156 | /usr/sbin/opendmarc-import --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose < ${WORK_DIR}/opendmarc_import.dat
157 | /usr/sbin/opendmarc-reports --dbhost=${DB_SERVER} --dbuser=${DB_USER} --dbpasswd=${DB_PASS} --dbname=${DB_NAME} --verbose --interval=86400 --report-email $REPORT_EMAIL --report-org $REPORT_ORG
158 | ```
159 |
160 | Make sure the script is executable
161 |
162 | chmod +x /etc/opendmarc/report_script
163 |
164 | and run it every day under the user opendmarc by adding the following to **/etc/crontab**:
165 |
166 | 1 0 * * * opendmarc /etc/opendmarc/report_script
167 |
168 | ### Configuring SpamAssassin
169 | SpamAssassin uses a scoring mechanism in order to determine if an e-mail should be considered spam. By default SpamAssassin considers an e-mail to be spam if the score at least "5". An e-mail starts with a score of 0 and points are added based on the [tests](https://spamassassin.apache.org/old/tests_3_3_x.html) performed. The tests performed can be configured by adding specific [configuration parameters](https://spamassassin.apache.org/full/3.4.x/doc/Mail_SpamAssassin_Conf.html) in **/etc/spamassassin/local.cf**.
170 |
171 | Now here's the tricky part. The points added to the score of an incoming e-mail based on the results of a specific test, is at its core a custom job. Many variables can be taken into consideration when scoring an e-mail (which is considered the strength of a post-SMTP spam filter) and the detailed scoring depends on a domain owner's specific wishes. For the sake of this how-to, the DMARC scoring will be based on the assumption that the domain owner wants to consider an e-mail to be spam if the sending e-mail server's DMARC validation did fail.
172 |
173 | With SpamAssassin this can be configured by adding the following scoring configuration parameters to **/etc/spamassassin/local.cf**:
174 |
175 | ```
176 | #dmarc fail
177 | header CUST_DMARC_FAIL Authentication-Results =~ /mail\.example\.nl; dmarc=fail/
178 | score CUST_DMARC_FAIL 5.0
179 |
180 | #dmarc pass
181 | header CUST_DMARC_PASS Authentication-Results =~ /mail\.example\.nl; dmarc=pass/
182 | score CUST_DMARC_PASS -1.0
183 | ```
184 |
185 | This means that when the "Authentication-Results" header of your e-mail contains "mail.example.nl; dmarc=fail" 5 points will be added to the score; instantly classifying this e-mail as SPAM. On the other hand, if the "Authentication-Results" header of your e-mail contains "mail.example.nl; dmarc=pass" -1 points will be added to the score; classifying this e-mail as legitimate.
186 |
187 | # Special thanks
188 | Our infinite gratitude goes out to the following people for their support in building this how-to for DANE.
189 |
190 | Alwin de Bruin
191 |
--------------------------------------------------------------------------------
/LICENSE-CC-BY-4.0.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Attribution 4.0 International
2 |
3 | Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
4 |
5 | Using Creative Commons Public Licenses
6 |
7 | Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
8 |
9 | Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors
10 |
11 | Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public : wiki.creativecommons.org/Considerations_for_licensees
12 |
13 | Creative Commons Attribution 4.0 International Public License
14 |
15 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
16 |
17 | Section 1 – Definitions.
18 |
19 | a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
20 | b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
21 | c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
22 | d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
23 | e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
24 | f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
25 | g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
26 | h. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
27 | i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
28 | j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
29 | k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
30 |
31 | Section 2 – Scope.
32 |
33 | a. License grant.
34 | 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
35 | A. reproduce and Share the Licensed Material, in whole or in part; and
36 | B. produce, reproduce, and Share Adapted Material.
37 | 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
38 | 3. Term. The term of this Public License is specified in Section 6(a).
39 | 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
40 | 5. Downstream recipients.
41 | A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
42 | B. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
43 | 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
44 | b. Other rights.
45 | 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
46 | 2. Patent and trademark rights are not licensed under this Public License.
47 | 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
48 |
49 | Section 3 – License Conditions.
50 |
51 | Your exercise of the Licensed Rights is expressly made subject to the following conditions.
52 |
53 | a. Attribution.
54 | 1. If You Share the Licensed Material (including in modified form), You must:
55 | A. retain the following if it is supplied by the Licensor with the Licensed Material:
56 | i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
57 | ii. a copyright notice;
58 | iii. a notice that refers to this Public License;
59 | iv. a notice that refers to the disclaimer of warranties;
60 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
61 | B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
62 | C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
63 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
64 | 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
65 | 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.
66 |
67 | Section 4 – Sui Generis Database Rights.
68 |
69 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
70 |
71 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
72 | b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and
73 | c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
74 |
75 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
76 |
77 | Section 5 – Disclaimer of Warranties and Limitation of Liability.
78 |
79 | a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
80 | b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
81 | c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
82 |
83 | Section 6 – Term and Termination.
84 |
85 | a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
86 | b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
87 | 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
88 | 2. upon express reinstatement by the Licensor.
89 | c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
90 | d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
91 | e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
92 |
93 | Section 7 – Other Terms and Conditions.
94 |
95 | a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
96 | b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
97 |
98 | Section 8 – Interpretation.
99 |
100 | a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
101 | b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
102 | c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
103 | d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
104 |
105 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
106 |
107 | Creative Commons may be contacted at creativecommons.org.
108 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Welcome to the [Internet.nl](https://internet.nl) toolbox.
4 |
5 | This GitHub repository contains several how-to's for providing practical information and guidance on implementing secure and modern Internet Standards. The how-to's are maintained by the Dutch Internet Standards Platform (the organization behind [Internet.nl](https://internet.nl)) and are created in cooperation with industry experts and enthusiasts (hosters, vendors, etc).
6 |
7 | Feedback and/or contributions are much appreciated and welcome through issues, pull requests or via question@internet.nl.
8 |
9 | # Quick access
10 | [DANE how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/DANE-for-SMTP-how-to.md)
11 | [DKIM how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/DKIM-how-to.md)
12 | [SPF how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/SPF-how-to.md)
13 | [DMARC how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/DMARC-how-to.md)
14 | [Parked domain how-to](https://github.com/internetstandards/toolbox-wiki/blob/master/parked-domain-how-to.md)
15 |
16 | # Interesting external sources
17 | [SIDN Hands-on guides](https://www.sidn.nl/en/cyber-security/modern-internet-standards)
18 | [The Internet Society's Open Standards Everywhere Project](https://github.com/InternetSociety/ose-documentation)
19 |
--------------------------------------------------------------------------------
/SPF-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # SPF how-to
4 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on implementing SPF.
5 |
6 | # Table of contents
7 | - [What is SPF?](#what-is-spf-)
8 | - [Why use SPF?](#why-use-spf-)
9 | - [Tips, tricks and notices for implementation](#tips--tricks-and-notices-for-implementation)
10 | - [Outbound e-mail traffic (DNS records)](#outbound-e-mail-traffic--dns-records-)
11 | - [Inbound e-mail traffic](#inbound-e-mail-traffic)
12 | * [Implementing SPF in Postfix with SpamAssassin](#implementing-spf-in-postfix-with-spamassassin)
13 | + [Configuring Postfix](#configuring-postfix)
14 | - [Postfix configuration for Python SPF policy agent](#postfix-configuration-for-python-spf-policy-agent)
15 | - [Postfix configuration for SpamAssassin](#postfix-configuration-for-spamassassin)
16 | + [Configuring Python SPF policy agent](#configuring-python-spf-policy-agent)
17 | + [Configuring SpamAssassin](#configuring-spamassassin)
18 |
19 | Table of contents generated with markdown-toc
20 |
21 | # What is SPF?
22 | SPF is short for "**S**ender **P**olicy **F**ramework" and is described in [RFC 7208](https://tools.ietf.org/html/rfc7208). It offers domain owners that use their domains for sending e-mail, the possibility to use the DNSSEC infrastructure to publish which hosts (mail servers) are authorized to use their domain names in the "MAIL FROM" and "HELO" identities. So basically SPF is a whitelist which lists all servers that are allowed to send e-mail on behalf of a specific domain. The receiving mail server may use the information (a SPF record) published in the DNS zone of a specific mail sending domain.
23 |
24 | # Why use SPF?
25 | Our current e-mail infrastructure was originally designed for any mail sending host to use any DNS domain name it wants. The authenticity of the sending mail server cannot be deterimined, which makes it easy for random third parties to make use of a domain name with possibly a malicious intent. This increases the risk of processing e-mail since the intentions of the sender (host) are uncertain. SPF can help the fight against spam and other kinds of unwanted e-mail be offering a way of authenticating the sending mail server.
26 |
27 | # Tips, tricks and notices for implementation
28 | * The sender address shown to the user ("RFC5322.From") is not used when authenticating. SPF uses the invisible "RFC5321.MailFrom" header. Combining SPF with DMARC removes this disadvantage.
29 | * E-mail forwarding is not supported, since the e-mail is often forwarded by another e-mail server.
30 | * SPF does not work between domains that use the same e-mail server.
31 | * Parked domains should be explicitly configured to not use e-mail. For SPF this is done with an empty policy (not mentioning any ip-adresses or hostnames which are allowed to send mail) and a hard fail: "v=spf1 –all".
32 | * When processing incoming mail we advise to favor a DMARC policy over an SPF policy. Do not configure SPF rejection to go into effect early in handling, but take full advantage of the enhancements DMARC is offering. A message might still pass based on DKIM.
33 | * At the same time, be aware that some operaters still allow a hard fail (-all) to go into effect early in handling and skip DMARC operations.
34 | * As stated in [section 5.2 of the RFC](https://tools.ietf.org/html/rfc7208#section-5.2) the _include_ mechanism is not applicable to the _all_ mechanism within the referenced record. This means that an SPF record's default policy is not 'inherited' upon inclusion. When including one or more SPF records from other domains, a default policy (~all or -all) is still required. For fully 'inheriting' another domain's SPF record, consider using the [_redirect_ modifier](https://tools.ietf.org/html/rfc7208#section-6.1).
35 |
36 | # Outbound e-mail traffic (DNS records)
37 | SPF for outbound e-mail traffic is limited to publishing an SPF policy as a TXT-record in a domain name's DNS zone. This enables other parties to use SPF for validating the authenticity of e-mail servers sending e-mail on behalf of your domain name.
38 |
39 | The example below shows an SPF record with a **hard fail**.
40 | > v=spf1 mx ip4:192.168.1.1/32 ip6:fd12:3456:789a:1::/64 a:mail.example.com a:mail2.example.com -all"
41 |
42 | Although a soft fail (~all) is recommended in order to prevent false positives, a hard fail (-all) could decreases the load on the receiving party's e-mail infrastructure (depending on their configuration) since the e-mail can be dropped immediately when the e-mail is send by a host or IP-address that is not listed in the SPF record.
43 |
44 | # Inbound e-mail traffic
45 | Ideally incoming e-mail is processed by making a **single decision** based on a collective evaluation of all relevant e-mail standards (SPF, DKIM, DMARC). Although this would be the most elegant way of processing incoming e-mail, most e-mail servers process e-mail standards in a sequential order. This should be taken into consideration when configuring your own e-mail environment; depending on a domain owner's preferences it is also possible to implement a "single decision" e-mail environment.
46 |
47 | Thereafter, it is [stated in the DMARC RFC](https://tools.ietf.org/html/rfc7489#section-10.1) that some receiver architectures might implement SPF in advance of any DMARC operations. This means that a "-" prefix on a sender's SPF mechanism, such as "-all", could cause that rejection to go into effect early in handling, causing message rejection before any DMARC processing takes place. While operators choosing to use "-all" should be aware of this, we advise to favor a DMARC policy over an SPF policy. As also [stated in the DMARC RFC](https://tools.ietf.org/html/rfc7489#section-6.7), the final diposition of a message is always a matter of local policy. With this in mind we feel that operators should not configure SPF rejection to go into effect early in handling, and thus disregarding DMARC. At the cost of processing the entire message body, we advise to always evaluate all relevant standards before coming to a decision. If SPF fails, DKIM might still pass resulting in a satisfying DMARC evaluation.
48 |
49 | ## Implementing SPF in Postfix with SpamAssassin
50 | **Specifics for this setup**
51 | * Linux Debian 9.8 (Stretch)
52 | * SpamAssassin version 3.4.2 (running on Perl version 5.28.1)
53 | * Postfix 3.4.5
54 | * BIND 9.10.3-P4-Debian
55 | * Python SPF policy agent (postfix-policyd-spf-python) 2.0.1-1
56 |
57 | **Assumptions**
58 | * DNSSEC is used
59 | * Mail server is operational
60 | * Software packages are already installed
61 |
62 | ### Configuring Postfix
63 | The [Postfix SMTP server](http://www.postfix.org/smtpd.8.html) seems to be processing e-mails in a sequential order by means of so-called [access restriction lists](http://www.postfix.org/SMTPD_ACCESS_README.html#lists). For each stage of the SMTP conversation Postfix can apply a specific set of restrictions. As repeatedly stated in the [main.cf man page](http://www.postfix.org/postconf.5.html) “Restrictions are applied in the order as specified; the first restriction that matches wins”. This should be taken into consideration when configuring your Postfix implementation.
64 |
65 | The follow table provides a schematic overview of an SMTP conversation and relates specific stages to Postfix' access restriction lists.
66 |
67 | | Part of the SMTP conversation | Associated Postfix access restriction list | Comments
68 | | --- | --- | --- |
69 | | Connected to 192.168.1.1 (192.168.1.1). | | |
70 | | Escape character is '^]'. | | |
71 | | 220 mail.example.com ESMTP Postfix | [smtpd_client_restrictions](http://www.postfix.org/postconf.5.html#smtpd_client_restrictions) | |
72 | | HELO mail.example.com | [smtpd_helo_restrictions](http://www.postfix.org/postconf.5.html#smtpd_helo_restrictions) | |
73 | | 250 mail.example.com | | |
74 | | MAIL FROM: | [smtpd_sender_restrictions](http://www.postfix.org/postconf.5.html#smtpd_sender_restrictions) | Aliases: RFC5321.MailFrom, Return-Path, Envelope Sender, Envelope From Used by **SPF** |
75 | | 250 2.1.0 Ok | | |
76 | | RCPT TO: | [smtpd_recipient_restrictions](http://www.postfix.org/postconf.5.html#smtpd_recipient_restrictions) | |
77 | | 250 2.1.5 Ok | | |
78 | | DATA | [smtpd_data_restrictions](http://www.postfix.org/postconf.5.html#smtpd_data_restrictions) | | |
79 | | 354 End data with . | | |
80 | | To: | | header checks |
81 | | From: | | Aliases: RFC5322.From, Header From, Message From Used by DKIM |
82 | | Subject: Test message | | |
83 | | This is a test message | | body checks |
84 | | . | | |
85 | | 250 2.0.0 Ok: queued as 534AB003427 | | |
86 | | QUIT | | |
87 | | 221 2.0.0 Bye | | |
88 | | Connection closed by foreign host. | | |
89 |
90 | #### Postfix configuration for Python SPF policy agent
91 | The implementation described in this how-to uses an external application to perform SPF checking: Python SPF policy agent (postfix-policyd-spf-python). In order for Postfix to be able to use this application, the following needs to be added to **/etc/postfix/master.cf**:
92 |
93 | `policy-spf unix - n n - - spawn user=nobody argv=/usr/bin/policyd-spf`
94 |
95 | This creates the service "policy-spf" that can now be called upon by Postfix from within the access restriction lists _smtpd_client_restrictions_ and _smtpd_recipient_restrictions_. This requires the following additions to **/etc/postfix/main.cf**. Make sure to call for the policy-spf service before the "permit".
96 |
97 | ```
98 | smtpd_recipient_restrictions =
99 | check_policy_service unix:private/policy-spf,
100 | permit
101 |
102 | smtpd_client_restrictions =
103 | check_policy_service unix:private/policy-spf,
104 | permit
105 | ```
106 |
107 | Now also add the following to **/etc/postfix/main.cf**, outside of any section.
108 |
109 | `policy-spf_time_limit = 3600s`
110 |
111 | #### Postfix configuration for SpamAssassin
112 | Because this implementation uses SpamAssassin for post-SMTP spam filtering, the following needs to be added to /etc/postfix/master.cf:
113 |
114 | ```
115 | smtp inet n - - - - smtpd -o content_filter=spamassassin
116 | spamassassin unix - n n - - pipe user=debian-spamd argv=/usr/bin/spamc -f -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}
117 | ```
118 | Finally, add the following to **/etc/postfix/main.cf** outside of any section to prevent Postfix from expecting SpamAssassin to parse multiple recipients at once (which is not supported).
119 |
120 | `spamassassin_destination_recipient_limit = 1`
121 |
122 | ### Configuring Python SPF policy agent
123 | The next step is to tell the Python SPF policy agent how to behave when checking SPF records. This behavior is determined by adding [configuration parameters](https://manpages.debian.org/stretch/postfix-policyd-spf-python/policyd-spf.conf.5.en.html) to **/etc/postfix-policyd-spf-python/policyd-spf.conf**.
124 |
125 | The default configuration of the Python SPF policy agent provides a binary "block" or "don't block" functionality. However, the implementation described in this how-to uses SpamAssassin as a post-SMTP spam filter. This means that Postfix should not reject e-mails coming from e-mail servers that are not listed in the SPF record. Instead an SPF header is appended to the e-mail. The information in the header is used by SpamAssassin to weigh whether an incoming e-mail should be considered spam. This specific setup requires the following non-default configuration parameters in **/etc/postfix-policyd-spf-python/policyd-spf.conf**:
126 |
127 | ```
128 | HELO_reject = False
129 | Mail_From_reject = False
130 | ```
131 |
132 | ### Configuring SpamAssassin
133 | SpamAssassin uses a scoring mechanism in order to determine if an e-mail should be considered spam. By default SpamAssassin considers an e-mail to be spam if the score at least "5". An e-mail starts with a score of 0 and points are added based on the [tests](https://spamassassin.apache.org/old/tests_3_3_x.html) performed. The tests performed can be configured by adding specific [configuration parameters](https://spamassassin.apache.org/full/3.4.x/doc/Mail_SpamAssassin_Conf.html) in **/etc/spamassassin/local.cf**.
134 |
135 | Now here's the tricky part. The points added to the score of an incoming e-mail based on the results of a specific test, is at its core a custom job. Many variables can be taken into consideration when scoring an e-mail (which is considered the strength of a post-SMTP spam filter) and the detailed scoring depends on a domain owner's specific wishes. For the sake of this how-to, the SPF scoring will be based on the assumption that the domain owner wants to consider an e-mail to be spam if the sending e-mail server's IP-address or host is not in the domain's SPF record.
136 |
137 | With SpamAssassin this can be configured by adding the following scoring configuration parameters to **/etc/spamassassin/local.cf**:
138 |
139 | ```
140 | score SPF_FAIL 5.0
141 | score SPF_SOFTFAIL 5.0
142 | score SPF_HELO_FAIL 5.0
143 | score SPF_HELO_SOFTFAIL 5.0
144 | ```
145 |
146 | This means that either a soft fail (~all) or a hard fail (-all) policy will result in the e-mail being classified as spam if the IP-address or host is not in the domain's SPF record.
147 |
--------------------------------------------------------------------------------
/TLS-config_spreadsheet/20210506_TLS-config_NCSC-NL.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/TLS-config_spreadsheet/20210506_TLS-config_NCSC-NL.ods
--------------------------------------------------------------------------------
/TLS-config_spreadsheet/20210506_TLS-config_NCSC-NL.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/TLS-config_spreadsheet/20210506_TLS-config_NCSC-NL.xlsx
--------------------------------------------------------------------------------
/images/DANE-example-TLSA-record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/DANE-example-TLSA-record.png
--------------------------------------------------------------------------------
/images/dane-example-1-evilcert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-example-1-evilcert.png
--------------------------------------------------------------------------------
/images/dane-example-1-no-dane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-example-1-no-dane.png
--------------------------------------------------------------------------------
/images/dane-example-1-striptls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-example-1-striptls.png
--------------------------------------------------------------------------------
/images/dane-example-1-with-dane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-example-1-with-dane.png
--------------------------------------------------------------------------------
/images/dane-halon-dnssec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-halon-dnssec.png
--------------------------------------------------------------------------------
/images/dane-halon-inbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-halon-inbound.png
--------------------------------------------------------------------------------
/images/dane-halon-outbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dane-halon-outbound.png
--------------------------------------------------------------------------------
/images/dkim-halon-dns-record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dkim-halon-dns-record.png
--------------------------------------------------------------------------------
/images/dkim-halon-private-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dkim-halon-private-key.png
--------------------------------------------------------------------------------
/images/dkim-halon-public-key-details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/internetstandards/toolbox-wiki/d8aded7416f77f8bf6091d5ec249ccc2c285ef0a/images/dkim-halon-public-key-details.png
--------------------------------------------------------------------------------
/images/logo-internetnl-en.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/parked-domain-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Parked domain how-to
4 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on explicitly configuring a parked domain not to use e-mail.
5 |
6 | # Table of contents
7 | - [What is a parked domain?](#what-is-a-parked-domain-)
8 | - [Domain without e-mail](#domain-without-e-mail)
9 | * [Null MX](#null-mx)
10 | * [DMARC](#dmarc)
11 | * [DKIM](#dkim)
12 | * [SPF](#spf)
13 | - [Domain without a website](#domain-without-a-website)
14 |
15 | Table of contents generated with markdown-toc
16 |
17 | # What is a parked domain?
18 | [Domain parking](https://en.wikipedia.org/wiki/Domain_parking) is the registration of an Internet domain name without that domain being associated with any services such as e-mail or a website.
19 |
20 | # Domain without e-mail
21 | If a domain is not using e-mail it is recommended to use the following settings.
22 |
23 | ## Null MX
24 | Explicitly configure an 'empty' MX record according to [RFC7505 ](https://tools.ietf.org/html/rfc7505).
25 |
26 | `example.nl. IN MX 0 .`
27 |
28 | ## DMARC
29 | Set DMARC policy to reject mails, but allow reporting to take place. This helps detecting activity related to your domain.
30 |
31 | `_dmarc.example.nl. IN TXT "v=DMARC1; p=reject; rua=mailto:rua@example.nl; ruf=mailto:ruf@example.nl"`
32 |
33 | If the domain itself does not receive email (such as when the domain has been configured with NULL MX), then the RUA and RUF must point to another domain that does receive emails, such as:
34 |
35 | `_dmarc.example.nl. TXT "v=DMARC1; p=reject; rua=mailto:rua@example.net; ruf=mailto:ruf@example.net"`
36 |
37 | On the other domain (that does receive e-mail), add an authorization record for the parked domain:
38 |
39 | `example.nl._report._dmarc.example.net IN TXT "v=DMARC1;"`
40 |
41 | ## DKIM
42 | When using a wildcard selector to set an empty public key, you indicate that all previously used keys are revoked and must be considered unreliable. You can also use this to explicitly signal that a domain is not configured to use e-mail. However, [according to the RFC](https://tools.ietf.org/html/rfc6376#section-6.1.2) the absence of a selector / public key (e.g. as a result of deleting the entire DKIM resource record) is semantically equal to a resource record with an empty public key. This means that both approaches should be treated similar by the receiving mail server.
43 |
44 | `*._domainkey.example.nl. IN TXT "v=DKIM1; p="`
45 |
46 | ## SPF
47 | Set an an empty policy (not mentioning any ip-adresses or hostnames which are allowed to send mail) and a hard fail.
48 |
49 | `example.nl. IN TXT "v=spf1 –all"`
50 |
51 | # Domain without a website
52 | Apply the following settings to domains not using a website.
53 |
54 | * Don't use an A or AAAA record for parked domains.
55 | * Don't redirect from a parked domain to the used domain, since this encourages users to keep using the parked domain name. If a redirect is desirable, make sure to use the proper redirect order in order for HSTS headers to remain effective:
56 | 1. redirect from HTTP to HTTPS on the same (sub)domain.
57 | 2. when using HTTPS, redirect to another (sub)domain.
58 |
--------------------------------------------------------------------------------
/under construction/DNS-records-overview.md:
--------------------------------------------------------------------------------
1 | This document lists the basic usage of commonly used DNS records. It can be used to track commonly made mistakes.
2 |
3 | # A
4 | * Points to an IPv4 address.
5 | * Does not point to anyhting else.
6 | * Record does not start (left side) with _ or -.
7 |
8 | # AAAA
9 | * Points to an IPv6 address.
10 | * Does not point to anyhting else.
11 | * Record does not start (left side) with _ or -.
12 |
13 | # MX
14 | * Points to an A and/or AAAA record.
15 | * Preferrably does not point to other record types, but the use of CNAME records is seen in practice. RFC's are inconsistent on this.
16 | * Has a priority value.
17 |
18 | # CNAME
19 | * Points to other records (A, AAAA, CNAME).
20 | * Be carefull with CNAME chaining; don't use too many CNAMEs in a row.
21 | * The end of a CNAME chain is always an A an/or AAAA record.
22 | * **Can only be combined with NS / SOA records if left side is equal.**
23 |
24 | # SRV
25 | * Records starts (left side) with _.
26 | * Points to an A and/or AAAA record.
27 | * Has a priority value.
28 |
29 | # NS
30 | * Points to an A and/or AAAA record.
31 | * **Used to point to subzones.**
32 | * **Used to indicate what is inside the parent zone.**
33 | * **Each zone needs this to indicate what is inside the parent zone as a reference to this zone.**
34 |
35 | # SOA
36 | * Mandatory for every DNS zone.
37 | * Contains the following information (seperated by a single white space):
38 | * FQDN of the primairy name server followed by a trailing dot.
39 | * e-mail address of the DNS administrator (followed by a trailing dot, the @ replaced with a dot).
40 | * an opening round bracket "(".
41 | * serial number that is changed (increased) on every zone change.
42 | * refresh time (in seconds) for a secondary name server to check the primairy name server for changes in the zone.
43 | * retry time (in seconds) for a secondary name server for requesting the serial number when the primary name server did not respond on the previous request.
44 | * expire time (in seconds) after which a secondary name server should stop answering requests if the primary name server is not responding.
45 | * Negative caching time to live (in seconds) which is the time a caching name server should cache a negative result (indicating that a name does not exist) coming from the authorative name server before trying again.
46 | * a closing round bracket ")".
47 |
48 |
--------------------------------------------------------------------------------
/under construction/Nginx-webserver-100%-example-config.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Ngnix example configuration
4 | This example configuration was created by SIDN Labs and shows how to configure Ngnix in order to score 100% in the Website test on Internet.nl.
5 |
6 | # Assumptions
7 | * DNSSEC is used
8 | * IPv6 is used
9 |
10 | # Configuring Ngnix
11 |
12 | server_tokens off;
13 | add_header X-Frame-Options SAMEORIGIN;
14 | add_header X-Content-Type-Options nosniff;
15 | add_header X-XSS-Protection "1; mode=block";
16 | add_header Strict-Transport-Security "max-age=31536000" always;
17 | add_header Content-Security-Policy "default-src 'self'; frame-ancestors 'none'" always;
18 | add_header Referrer-Policy "strict-origin" always;
19 |
20 | ssl_stapling on;
21 | ssl_stapling_verify on;
22 | ssl_trusted_certificate /etc/letsencrypt/live/example.nl/fullchain.pem;
23 |
24 | # Rate limiting 20 requests/s
25 | limit_req_zone $binary_remote_addr zone=mylimit:10m rate=20r/s;
26 |
27 | # http://example.nl -> https://example.nl
28 | server {
29 | listen 80;
30 | listen [::]:80;
31 | server_name example.nl;
32 |
33 | location /.well-known/acme-challenge/ {
34 | root /var/www/certbot;
35 | }
36 |
37 | location / {
38 | return 301 https://example.nl$request_uri;
39 | }
40 | }
41 |
42 | # http://(www|api).example.nl -> https://(www|api).example.nl
43 | server {
44 | listen 80;
45 | listen [::]:80;
46 | server_name www.example.nl api.example.nl;
47 |
48 | return 301 https://$host$request_uri;
49 | }
50 |
51 | # https://example.nl -> https://www.example.nl
52 | server {
53 | listen 443 ssl http2;
54 | listen [::]:443 ssl http2;
55 | server_name example.nl;
56 |
57 | ssl_certificate /etc/letsencrypt/live/example.nl/fullchain.pem;
58 | ssl_certificate_key /etc/letsencrypt/live/example.nl/privkey.pem;
59 |
60 | ssl_session_cache shared:SSL:50m;
61 | ssl_session_timeout 1d;
62 | ssl_session_tickets on;
63 |
64 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
65 |
66 | ssl_protocols TLSv1.2 TLSv1.3;
67 | ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
68 | ssl_prefer_server_ciphers on;
69 |
70 | return 301 https://www.$host$request_uri;
71 | }
72 |
73 | # serve https://(www|api).example.nl
74 | server {
75 | listen 443 ssl http2;
76 | listen [::]:443 ssl http2;
77 | server_name www.example.nl api.example.nl;
78 |
79 | ssl_certificate /etc/letsencrypt/live/example.nl/fullchain.pem;
80 | ssl_certificate_key /etc/letsencrypt/live/example.nl/privkey.pem;
81 |
82 | ssl_session_cache shared:SSL:50m;
83 | ssl_session_timeout 1d;
84 | ssl_session_tickets on;
85 |
86 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
87 |
88 | ssl_protocols TLSv1.2 TLSv1.3;
89 | ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
90 | ssl_prefer_server_ciphers on;
91 |
92 | # Example location for a Flask application running on port 8080
93 | location / {
94 | limit_req zone=mylimit burst=100 nodelay;
95 |
96 | include uwsgi_params;
97 | uwsgi_pass flask:8080;
98 | uwsgi_ignore_client_abort on;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/under construction/STARTTLS-how-to.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # UNDER CONSTRUCTION!!!
4 |
5 | # STARTTLS how-to
6 | This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on implementing STARTTLS.
7 |
8 | # Table of contents
9 | Under construction
10 |
11 | # What is STARTTLS?
12 | Under construction
13 |
14 | # Why use STARTTLS?
15 | Under construction
16 |
17 | # Tips, tricks and notices for implementation
18 | * http://postfix.1071664.n5.nabble.com/Disable-SSL-TLS-renegotiation-td96864.html#a96871
19 | * Use the RFC 7919 defined DH groups: https://raw.githubusercontent.com/internetstandards/dhe_groups/master/ffdhe4096.pem)
20 |
21 | ## Implementing STARTTLS in Postfix
22 | **Specifics for this setup**
23 | * Linux Debian 10 (Buster)
24 | * Postfix 3.4.5
25 | * OpenSSL 1.1.1d
26 |
27 | **Assumptions**
28 | * Mail server is using DANE
29 | * Software packages are already installed
30 |
31 | ### Configuring Postfix
32 |
33 | # use DANE (when acting as a client)
34 | smtp_dns_support_level = dnssec
35 | smtp_tls_security_level = dane
36 | smtp_host_lookup = dns
37 | smtp_tls_note_starttls_offer = yes
38 |
39 | # --- TLS settings ---
40 | smtpd_tls_security_level = may
41 | smtpd_tls_key_file = /etc/postfix/ssl/example.nl.key
42 | smtpd_tls_cert_file = /etc/postfix/ssl/example.nl.crt
43 | smtpd_tls_CAfile = /etc/postfix/ssl/example.nl-cabundle.crt
44 | smtpd_tls_received_header = yes
45 | smtpd_tls_session_cache_timeout = 3600s
46 |
47 | # --- TLS protocol config ---
48 | smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
49 | smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
50 | smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
51 | smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
52 | lmtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
53 | lmtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
54 |
55 | # --- TLS cipher config ---
56 | smtpd_tls_mandatory_ciphers=high
57 | smtpd_tls_ciphers=high
58 | # disable compression and client-initiated renegotiation
59 | tls_ssl_options = NO_COMPRESSION, 0x40000000
60 | # disable unsecure ciphers
61 | smtpd_tls_exclude_ciphers = EXP, LOW, MEDIUM, aNULL, eNULL, SRP, PSK, kDH, ADH, AECDH, kRSA, DSS, RC4, DES, IDEA, SEED, ARIA, AESCCM8, 3DES, MD5
62 | smtp_tls_exclude_ciphers = EXP, LOW, MEDIUM, aNULL, eNULL, SRP, PSK, kDH, ADH, AECDH, kRSA, DSS, RC4, DES, IDEA, SEED, ARIA, AESCCM8, 3DES, MD5
63 | # Enable server cipher-suite preferences
64 | tls_preempt_cipherlist = yes
65 | # Forward secrecy
66 | smtpd_tls_eecdh_grade=ultra
67 | smtpd_tls_dh1024_param_file = /etc/postfix/ssl/ffdhe4096.pem
68 |
69 | # --- TLS logging ---
70 | smtp_tls_loglevel = 1
71 | smtpd_tls_loglevel = 1
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------