Added -E option to preserve the domain name in the final SPF record

This commit is contained in:
nicobo 2024-08-23 12:23:38 +02:00
parent 786b4fe8f9
commit afe76e2bea
2 changed files with 46 additions and 14 deletions

View file

@ -16,11 +16,38 @@ Other useful resources on SPF :
- [SPF Record Syntax](https://dmarcian.com/spf-syntax-table/)
- [Can I have a TXT or SPF record longer than 255 characters?](https://kb.isc.org/docs/aa-00356)
## Current manual
usage: gandi-flatten-spf.py [-h] -d DOMAIN [DOMAIN ...] -e EMAIL_PROVIDERS [EMAIL_PROVIDERS ...] -E EMAIL_PROVIDERS_AS_IS [EMAIL_PROVIDERS_AS_IS ...] [-k API_KEY] [-r DNS [DNS ...]] [-l LOG_LEVEL]
[-L LOAD] [-K]
Flatten SPF records using Gandi's Live DNS API
options:
-h, --help show this help message and exit
-d DOMAIN [DOMAIN ...], --domain DOMAIN [DOMAIN ...]
Domains you own from which to update the TXT record for SPF
-e EMAIL_PROVIDERS [EMAIL_PROVIDERS ...], --email-providers EMAIL_PROVIDERS [EMAIL_PROVIDERS ...]
E-mail providers' SPF domains to add to the TXT record AFTER CONVERSION to a list of IP addresses
-E EMAIL_PROVIDERS_AS_IS [EMAIL_PROVIDERS_AS_IS ...], --email-providers-as-is EMAIL_PROVIDERS_AS_IS [EMAIL_PROVIDERS_AS_IS ...]
E-mail providers' SPF domains to add to the TXT record AS-IS (no IP conversion)
-k API_KEY, --api-key API_KEY
Your Gandi API key (otherwise looks for the 'GANDI_APIKEY' environment variable)
-r DNS [DNS ...], --dns DNS [DNS ...]
DNS servers to use to resolve into IP addresses
-l LOG_LEVEL, --log-level LOG_LEVEL
Log level
-L LOAD, --load LOAD A JSON file to load the result from Gandi's API instead of calling the API
-K, --dry-run Dry-run mode (will not change the records, only print)
The `-E` option was added because some providers only check that their domain appears in the DNS entries, not the corresponding IP (wrongly, in my opinion), so you can preserve the domains in the final TXT record.
## Example
python gandi-flatten-spf.py -d mydomain.com -e _spf.mailfence.com _spf.google.com _spf.mail.yahoo.com _mailcust.gandi.net _spf.protonmail.ch -l DEBUG
python gandi-flatten-spf.py -d mydomain.com -e _spf.google.com _spf.mail.yahoo.com _mailcust.gandi.net _spf.protonmail.ch -E _spf.mailfence.com -l DEBUG
Run without arguments to show the full syntax (including how to pass your Gandi API key).
Run without arguments to show the full syntax for your version (including how to pass your Gandi API key).
Put in a *cron job* to run on a regular basis and check if there was any change in the IP addresses of the email providers.

View file

@ -48,23 +48,27 @@ def spf2ips(records, domain, resolvers):
"""
Builds a valid TXT record for SPF by resolving DNS into IP addresses
_emailProviders: list of SPF domains of the email providers you want to add to SPF
_emailProvidersToIp: list of SPF domains to convert to a list of IPs (of the email providers you want to add to SPF)
_emailProvidersToInclude: : list of SPF domains NOT to convert to IP
_nameServers: list of DNS you want to use to resolve domains into IP addresses
Returns the TXT record as a string
"""
def createFlatSpfRecord( _emailProviders, _nameServers ):
def createFlatSpfRecord( _emailProvidersToIp, _emailProvidersToInclude, _nameServers ):
dnsResolver = resolver.Resolver()
dnsResolver.nameservers = _nameServers
ips = []
for emailProvider in _emailProviders:
parts = []
ips = ips + spf2ips({emailProvider: 'txt'}, emailProvider, dnsResolver)
for emailProvider in _emailProvidersToIp:
parts = parts + spf2ips({emailProvider: 'txt'}, emailProvider, dnsResolver)
logging.debug("Flattened IPs : %s",ips)
return SPF_TEMPLATE.format( includes=" ".join(ips) )
for emailProvider in _emailProvidersToInclude:
parts = parts + ["include:" + emailProvider]
logging.debug("Flattened IPs : %s",parts)
return SPF_TEMPLATE.format( includes=" ".join(parts) )
@ -187,8 +191,9 @@ def updateDns( _domain, _apikey, _txtRecords, _dryRun=False ):
parser = argparse.ArgumentParser(description="Flatten SPF records using Gandi's Live DNS API")
parser.add_argument('-d', '--domain', nargs='+', required=True, help="Domains you own from which to update the TXT record for SPF")
parser.add_argument('-e', '--email-providers', nargs='+', required=True, help="E-mail providers' SPF domains to add to the TXT record")
parser.add_argument('-k', '--apikey', help="Your Gandi API key (otherwise looks for the 'GANDI-APIKEY' environment variable)")
parser.add_argument('-e', '--email-providers', nargs='+', required=True, help="E-mail providers' SPF domains to add to the TXT record AFTER CONVERSION to a list of IP addresses")
parser.add_argument('-E', '--email-providers-as-is', nargs='+', required=True, help="E-mail providers' SPF domains to add to the TXT record AS-IS (no IP conversion)")
parser.add_argument('-k', '--api-key', help="Your Gandi API key (otherwise looks for the 'GANDI_APIKEY' environment variable)")
parser.add_argument('-r', '--dns', default=DEFAULT_DNS, nargs='+', help="DNS servers to use to resolve into IP addresses")
parser.add_argument('-l', '--log-level', default=DEFAULT_LOGLEVEL, help="Log level")
parser.add_argument('-L', '--load', help="A JSON file to load the result from Gandi's API instead of calling the API")
@ -199,14 +204,14 @@ logging.basicConfig(level=logging.getLevelName(args.log_level))
logging.debug(repr(args))
if args.apikey:
apikey = args.apikey
if args.api_key:
apikey = args.api_key
else :
apikey = os.environ['GANDI_APIKEY']
flatSpf = createFlatSpfRecord(args.email_providers,args.dns)
flatSpf = createFlatSpfRecord(args.email_providers,args.email_providers_as_is,args.dns)
for domain in args.domain:
if args.load:
txts = file_getTxtRecords(args.load)