github.com/teknogeek/dnscontrol/v2@v2.10.1-0.20200227202244-ae299b55ba42/docs/spf-optimizer.md (about) 1 --- 2 layout: default 3 title: SPF Optimizer 4 --- 5 6 # SPF Optimizer 7 8 dnscontrol can optimize the SPF settings on a domain by flattening 9 (inlining) includes and removing duplicates. dnscontrol also makes 10 it easier to document your SPF configuration. 11 12 **Warning:** Flattening SPF includes is risky. Only flatten an SPF 13 setting if it is absolutely needed to bring the number of "lookups" 14 to be less than 10. In fact, it is debatable whether or not ISPs 15 enforce the "10 lookup rule". 16 17 18 ## The old way 19 20 Here is an example of how SPF settings are normally done: 21 22 ``` 23 D("example.tld", REG, DNS, ... 24 TXT("v=spf1 ip4:198.252.206.0/24 ip4:192.111.0.0/24 include:_spf.google.com include:mailgun.org include:spf-basic.fogcreek.com include:mail.zendesk.com include:servers.mcsv.net include:sendgrid.net include:450622.spf05.hubspotemail.net ~all") 25 ) 26 ``` 27 28 This has a few problems: 29 30 * No comments. It is difficult to add a comment. In particular, we want to be able to list which ticket requested each item in the SPF setting so that history is retained. 31 * Ugly diffs. If you add an element to the SPF setting, the diff will show the entire line changed, which is difficult to read. 32 * Too many lookups. The SPF RFC says that SPF settings should not require more than 10 DNS lookups. If we manually flatten (i.e. "inline") an include, we have to remember to check back to see if the settings have changed. Humans are not good at that kind of thing. 33 34 ## The dnscontrol way 35 36 ``` 37 D("example.tld", REG, DSP, ... 38 A("@", "10.2.2.2"), 39 MX("@", "example.tld."), 40 SPF_BUILDER({ 41 label: "@", 42 overflow: "_spf%d", 43 raw: "_rawspf", 44 ttl: "5m", 45 parts: [ 46 "v=spf1", 47 "ip4:198.252.206.0/24", // ny-mail* 48 "ip4:192.111.0.0/24", // co-mail* 49 "include:_spf.google.com", // GSuite 50 "include:mailgun.org", // Greenhouse.io 51 "include:spf-basic.fogcreek.com", // Fogbugz 52 "include:mail.zendesk.com", // Zenddesk 53 "include:servers.mcsv.net", // MailChimp 54 "include:sendgrid.net", // SendGrid 55 "include:450622.spf05.hubspotemail.net", // Hubspot (Ticket# SREREQ-107) 56 "~all" 57 ], 58 flatten: [ 59 "spf-basic.fogcreek.com", // Rational: Being deprecated. Low risk if it breaks. 60 "450622.spf05.hubspotemail.net" // Rational: Unlikely to change without warning. 61 ] 62 }), 63 ); 64 ``` 65 66 By using the `SPF_BUILDER()` we gain many benefits: 67 68 * Comments can appear next to the element they refer to. 69 * Diffs will be shorter and more specific; therefore easier to read. 70 * Automatic flattening. We can specify which includes should be flattened and dnscontrol will do the work. It will even warn us if the includes change. 71 72 ## Syntax 73 74 When you want to specify SPF settings for a domain, use the 75 `SPF_BUILDER()` function. 76 77 ``` 78 D("example.tld", REG, DSP, ... 79 ... 80 ... 81 ... 82 SPF_BUILDER({ 83 label: "@", 84 overflow: "_spf%d", // Delete this line if you don't want big strings split. 85 raw: "_rawspf", // Delete this line if the default is sufficient. 86 parts: [ 87 "v=spf1", 88 // fill in your SPF items here 89 "~all" 90 ], 91 flatten: [ 92 // fill in any domains to inline. 93 ] 94 }), 95 ... 96 ... 97 ); 98 ``` 99 100 The parameters are: 101 102 * `label:` The label of the first TXT record. (Optional. Default: `"@"`) 103 * `overflow:` If set, SPF strings longer than 255 chars will be split into multiple TXT records. The value of this setting determines the template for what the additional labels will be named. If not set, no splitting will occur and dnscontrol may generate TXT strings that are too long. 104 * `raw:` The label of the unaltered SPF settings. (Optional. Default: `"_rawspf"`) 105 * `ttl:` This allows setting a specific TTL on this SPF record. (Optional. Default: using default record TTL) 106 * `parts:` The individual parts of the SPF settings. 107 * `flatten:` Which includes should be inlined. For safety purposes the flattening is done on an opt-in basis. If `"*"` is listed, all includes will be flattened... this might create more problems than is solves due to length limitations. 108 109 `SPR_BUILDER()` returns multiple `TXT()` records: 110 111 * `TXT("@", "v=spf1 .... ~all")` 112 * This is the optimized configuration. 113 * `TXT("_spf1", "...")` 114 * If the optimizer needs to split a long string across multiple TXT records, the additional TXT records will have labels `_spf1`, `_spf2`, `_spf3`, etc. 115 * `TXT("_rawspf", "v=spf1 .... ~all")` 116 * This is the unaltered SPF configuration. This is purely for debugging purposes and is not used by any email or anti-spam system. It is only generated if flattening is requested. 117 118 119 We recommend first using this without any flattening. Make sure 120 `dnscontrol preview` works as expected. Once that is done, add the 121 flattening required to reduce the number of lookups to 10 or less. 122 123 To count the number of lookups, you can use our interactive SPF 124 debugger at [https://stackexchange.github.io/dnscontrol/flattener/index.html](https://stackexchange.github.io/dnscontrol/flattener/index.html) 125 126 127 ## Notes about the DNS Cache 128 129 dnscontrol keeps a cache of the DNS lookups performed during 130 optimization. The cache is maintained so that the optimizer does 131 not produce different results depending on the ups and downs of 132 other people's DNS servers. This makes it possible to do `dnscontrol 133 push` even if your or third-party DNS servers are down. 134 135 The DNS cache is kept in a file called `spfcache.json`. If it needs 136 to be updated, the proper data will be written to a file called 137 `spfcache.updated.json` and instructions such as the ones below 138 will be output telling you exactly what to do: 139 140 ``` 141 $ dnscontrol preview 142 1 Validation errors: 143 WARNING: 2 spf record lookups are out of date with cache (_spf.google.com,_netblocks3.google.com). 144 Wrote changes to spfcache.updated.json. Please rename and commit: 145 $ mv spfcache.updated.json spfcache.json 146 $ git commit spfcache.json 147 ``` 148 149 In this case, you are being asked to replace `spfcache.json` with 150 the newly generated data in `spfcache.updated.json`. 151 152 Needing to do this kind of update is considered a validation error 153 and will block `dnscontrol push` from running. 154 155 Note: The instructions are hardcoded strings. The filenames will 156 not change. 157 158 Note: The instructions assume you use git. If you use something 159 else, please do the appropriate equivalent command. 160 161 ## Caveats: 162 163 1. Dnscontrol 'gives up' if it sees SPF records it can't understand. 164 This includes: syntax errors, features that our spflib doesn't know 165 about, overly complex SPF settings, and anything else that we we 166 didn't feel like implementing. 167 168 2. The TXT record that is generated may exceed DNS limits. dnscontrol 169 will not generate a single TXT record that exceeds DNS limits, but 170 it ignores the fact that there may be other TXT records on the same 171 label. For example, suppose it generates a TXT record on the bare 172 domain (stackoverflow.com) that is 250 bytes long. That's fine and 173 doesn't require a continuation record. However if there is another 174 TXT record (not an SPF record, perhaps a TXT record used to verify 175 domain ownership), the total packet size of all the TXT records 176 could exceed 512 bytes, and will require EDNS or a TCP request. 177 178 3. Dnscontrol does not warn if the number of lookups exceeds 10. 179 We hope to implement this some day. 180 181 4. The `redirect=` directive is only partially implemented. We only 182 handle the case where redirect is the last item in the SPF record. 183 In which case, it is equivalent to `include:`. 184 185 186 ## Advanced Technique: Interactive SPF Debugger 187 188 dnscontrol includes an experimental system for viewing 189 SPF settings: 190 191 [https://stackexchange.github.io/dnscontrol/flattener/index.html](https://stackexchange.github.io/dnscontrol/flattener/index.html) 192 193 You can also run this locally (it is self-contained) by opening 194 `dnscontrol/docs/flattener/index.html` in your browser. 195 196 You can use this to determine the minimal number of domains you 197 need to flatten to have fewer than 10 lookups. 198 199 The output is as follows: 200 201 1. The top part lists the domain as it current is configured, how 202 many lookups it requires, and includes a checkbox for each item 203 that could be flattened. 204 205 2. Fully flattened: This section shows the SPF configuration if you 206 fully flatten it. i.e. This is what it would look like if all the 207 checkboxes were checked. Note that this result is likely to be 208 longer than 255 bytes, the limit for a single TXT string. 209 210 3. Fully flattened split: This takes the "fully flattened" result 211 and splits it into multiple DNS records. To continue to the next 212 record an include is added. 213 214 215 ## Advanced Technique: Define once, use many 216 217 In some situations we define an SPF setting once and want to re-use 218 it on many domains. Here's how to do this: 219 220 ``` 221 var SPF_MYSETTINGS = SPF_BUILDER({ 222 label: "@", 223 overflow: "_spf%d", 224 raw: "_rawspf", 225 parts: [ 226 "v=spf1", 227 ... 228 "~all" 229 ], 230 flatten: [ 231 ... 232 ] 233 }); 234 235 D("example.tld", REG, DSP, ... 236 SPF_MYSETTINGS 237 ); 238 239 D("example2.tld", REG, DSP, ... 240 SPF_MYSETTINGS 241 ); 242 ```