github.com/teknogeek/dnscontrol@v0.2.8/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      parts: [
    45        "v=spf1",
    46        "ip4:198.252.206.0/24", // ny-mail*
    47        "ip4:192.111.0.0/24", // co-mail*
    48        "include:_spf.google.com", // GSuite
    49        "include:mailgun.org", // Greenhouse.io
    50        "include:spf-basic.fogcreek.com", // Fogbugz
    51        "include:mail.zendesk.com", // Zenddesk
    52        "include:servers.mcsv.net", // MailChimp
    53        "include:sendgrid.net", // SendGrid
    54        "include:450622.spf05.hubspotemail.net", // Hubspot (Ticket# SREREQ-107)
    55        "~all"
    56      ],
    57      flatten: [
    58        "spf-basic.fogcreek.com", // Rational: Being deprecated. Low risk if it breaks.
    59        "450622.spf05.hubspotemail.net" // Rational: Unlikely to change without warning.
    60      ]
    61    }),
    62  );
    63  ```
    64  
    65  By using the `SPF_BUILDER` we gain many benefits:
    66  
    67  * Comments can appear next to the element they refer to.
    68  * Diffs will be shorter and more specific; therefore easier to read.
    69  * 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.
    70  
    71  ## Syntax
    72  
    73  When you want to specify SPF settings for a domain, use the
    74  `SPF_BUILD()` function.
    75  
    76  ```
    77  D("example.tld", REG, DSP, ...
    78    ...
    79    ...
    80    ...
    81    SPF_BUILDER({
    82      label: "@",
    83      overflow: "_spf%d",  // Delete this line if you don't want big strings split.
    84      raw: "_rawspf",  // Delete this line if the default is sufficient.
    85      parts: [
    86        "v=spf1",
    87        // fill in your SPF items here
    88        "~all"
    89      ],
    90      flatten: [
    91        // fill in any domains to inline.
    92      ]
    93    }),
    94    ...
    95    ...
    96  );
    97  ```
    98  
    99  The parameters are:
   100  
   101  * `label:` The label of the first TXT record. (Optional. Default: `"@"`)
   102  * `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.
   103  * `raw:` The label of the unaltered SPF settings. (Optional. Default: `"_rawspf"`)
   104  * `parts:` The individual parts of the SPF settings.
   105  * `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.
   106  
   107  `SPR_BUILDER()` returns multiple `TXT()` records:
   108  
   109    * `TXT("@", "v=spf1 .... ~all")`
   110      *  This is the optimized configuration.
   111    * `TXT("_spf1", "...")`
   112      * If the optimizer needs to split a long string across multiple TXT records, the additional TXT records will have labels `_spf1`, `_spf2`, `_spf3`, etc.
   113    * `TXT("_rawspf", "v=spf1 .... ~all")`
   114      * 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.
   115  
   116  
   117  We recommend first using this without any flattening. Make sure
   118  `dnscontrol preview` works as expected. Once that is done, add the
   119  flattening required to reduce the number of lookups to 10 or less.
   120  
   121  To count the number of lookups, you can use our interactive SPF
   122  debugger at [https://stackexchange.github.io/dnscontrol/flattener/index.html](https://stackexchange.github.io/dnscontrol/flattener/index.html)
   123  
   124  
   125  ## Notes about the DNS Cache
   126  
   127  dnscontrol keeps a cache of the DNS lookups performed during
   128  optimization.  The cache is maintained so that the optimizer does
   129  not produce different results depending on the ups and downs of
   130  other people's DNS servers. This makes it possible to do `dnscontrol
   131  push` even if your or third-party DNS servers are down.
   132  
   133  The DNS cache is kept in a file called `spfcache.json`. If it needs
   134  to be updated, the proper data will be written to a file called
   135  `spfcache.updated.json` and instructions such as the ones below
   136  will be output telling you exactly what to do:
   137  
   138  ```
   139  $ dnscontrol preview
   140  1 Validation errors:
   141  WARNING: 2 spf record lookups are out of date with cache (_spf.google.com,_netblocks3.google.com).
   142  Wrote changes to spfcache.updated.json. Please rename and commit:
   143      $ mv spfcache.updated.json spfcache.json
   144      $ git commit spfcache.json
   145  ```
   146  
   147  In this case, you are being asked to replace `spfcache.json` with
   148  the newly generated data in `spfcache.updated.json`.
   149  
   150  Needing to do this kind of update is considered a validation error
   151  and will block `dnscontrol push` from running.
   152  
   153  Note: The instructions are hardcoded strings. The filenames will
   154  not change.
   155  
   156  Note: The instructions assume you use git. If you use something
   157  else, please do the appropriate equivalent command.
   158  
   159  ## Caveats:
   160  
   161  1. Dnscontrol 'gives up' if it sees SPF records it can't understand.
   162  This includes: syntax errors, features that our spflib doesn't know
   163  about, overly complex SPF settings, and anything else that we we
   164  didn't feel like implementing.
   165  
   166  2. The TXT record that is generated may exceed DNS limits.  dnscontrol
   167  will not generate a single TXT record that exceeds DNS limits, but
   168  it ignores the fact that there may be other TXT records on the same
   169  label.  For example, suppose it generates a TXT record on the bare
   170  domain (stackoverflow.com) that is 250 bytes long. That's fine and
   171  doesn't require a continuation record.  However if there is another
   172  TXT record (not an SPF record, perhaps a TXT record used to verify
   173  domain ownership), the total packet size of all the TXT records
   174  could exceed 512 bytes, and will require EDNS or a TCP request.
   175  
   176  3. Dnscontrol does not warn if the number of lookups exceeds 10.
   177  We hope to implement this some day.
   178  
   179  
   180  ## Advanced Technique: Interactive SPF Debugger
   181  
   182  dnscontrol includes an experimental system for viewing
   183  SPF settings:
   184  
   185  [https://stackexchange.github.io/dnscontrol/flattener/index.html](https://stackexchange.github.io/dnscontrol/flattener/index.html)
   186  
   187  You can also run this locally (it is self-contained) by opening
   188  `dnscontrol/docs/flattener/index.html` in your browser.
   189  
   190  You can use this to determine the minimal number of domains you
   191  need to flatten to have fewer than 10 lookups.
   192  
   193  The output is as follows:
   194  
   195  1. The top part lists the domain as it current is configured, how
   196  many lookups it requires, and includes a checkbox for each item
   197  that could be flattened.
   198  
   199  2. Fully flattened: This section shows the SPF configuration if you
   200  fully flatten it. i.e. This is what it would look like if all the
   201  checkboxes were checked. Note that this result is likely to be
   202  longer than 255 bytes, the limit for a single TXT string.
   203  
   204  3. Fully flattened split: This takes the "fully flattened" result
   205  and splits it into multiple DNS records.  To continue to the next
   206  record an include is added.
   207  
   208  
   209  ## Advanced Technique: Define once, use many
   210  
   211  In some situations we define an SPF setting once and want to re-use
   212  it on many domains. Here's how to do this:
   213  
   214  ```
   215  var SPF_MYSETTINGS = SPF_BUILDER({
   216    label: "@",
   217    overflow: "_spf%d",
   218    raw: "_rawspf",
   219    parts: [
   220      "v=spf1",
   221      ...
   222      "~all"
   223    ],
   224    flatten: [
   225      ...
   226    ]
   227  });
   228  
   229  D("example.tld", REG, DSP, ...
   230      SPF_MYSETTINGS
   231  );
   232  
   233  D("example2.tld", REG, DSP, ...
   234       SPF_MYSETTINGS
   235  );
   236  ```