├── .gitignore
├── README.md
├── docker-compose.yaml
├── expurgate-diagram.png
├── expurgate-rbldnsd
├── Dockerfile
└── running-config
├── expurgate-resolver
├── Dockerfile
├── output
│ ├── _netblocks-mimecast-com
│ ├── _spf-google-com
│ ├── google-xpg8-tk
│ ├── mailgun-org
│ ├── outbound-mailhop-org
│ ├── running-config
│ ├── sendgrid-net
│ ├── service-now-com
│ ├── spf-messagelabs-com
│ └── spf-protection-outlook-com
└── resolver.py
├── expurgate.png
├── expurgate.pptx
├── install.sh
└── python-vs-pypy.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | expurgate-single/Dockerfile
3 | expurgate-single/resolver.py
4 | expurgate-resolver/test.py
5 | test/test.py
6 | expurgate-resolver/build-and-run.bat
7 | expurgate-rbldnsd/build-and-run.bat
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | get 14 days free with bunny hosting Expurgate or Expurgate-Solo on Bunny's Magic Container Anycast edge computing solution [Signup with my bunny affiliate code here](https://bunny.net?ref=ndyy4946y2)
2 |
3 |
4 | 
5 |
6 | A dockerized multi-domain SPF hosting solution leveraging rbldnsd as the DNS server to simplify, hide and exceed SPF lookup limits. The resolver script runs periodically to generate SPF Macro friendly configuration files for rbldnsd.
7 |
8 | # What is Expurgate?
9 | expurgate
10 | /ˈɛkspəːɡeɪt/
11 | verb
12 | remove matter thought to be objectionable or unsuitable
13 |
14 | Expurgate is a passion project that provides the capability to host your own SPF flattening (aka compression) management solution. There is no webUI and no database. With the exception of copying your existing SPF record to a subdomain, the entire configuration is completed using ENV variables parsed at runtime. This solution will resolve your SPF records hosted on an unused subdomain that will act as the source of truth for expurgate-resolver and is how you will make changes when you need to add or remove IP, subnets and hostnames. Expurgate-resolver will detect the changes on the subdomain and publish the new rbldnsd configuration for expurgate-rbldnsd. In the example below the subdomain `_sd6sdyfn.yourdomain.com` is used. The Expurgate SPF macro is then published on the root domain in place of the old record. A script running on the spf-resolver container will loop through the records hosted on the subdomain (no issues if they exceed 10 lookups or are duplicates) and generate an rbldnsd configuration file of IP addressess and subnets that rbldnsd will use..
15 |
16 | Expurgate supports both IPv4 and IPv6 addresses.
17 |
18 | NOTE: SPF(Sender Policy Framework) records are DNS TXT records published by a domain owner at the root(@) of your domain so that e-mail sent from the domain owners, domains can be validated by the receiving MTA to check if the domain owner authorizes the transmission. SPF does not prevent spoofing as it specifically relates to the domain name in the `ENVELOPE FROM:` address and according to `RFC 7208` standard the EHLO domain. The recipient (end user) of the e-mail will most likley never see the `ENVELOPE FROM:` address or EHLO domain so it is possible to PASS SPF but still spoof the domain in the `HEADER FROM:` address that the recipient will see. A newer protocol called DMARC `RFC 7489` relies heavily on the SPF (and DKIM) protocol to prevent spoofing.
19 |
20 | You can read more on SPF here:
21 | https://en.wikipedia.org/wiki/Sender_Policy_Framework#Principles_of_operation
22 |
23 | # The problem
24 | SPF records are publicly visible, prone to misconfiguration and limited to include 10 host DNS resolutions which could be A records, MX records or other TXT records called INCLUDE's. While you may only `INCLUDE` one other domain e.g. _spf.google.com this may very well link to 2 or 3 other hostnames which all count toward the RFC limit of 10. Further risk is that 3rd party providers in your SPF record may add a new host without communicating the specifics and while you have been keeping on top of your record, this could unknowingly push you over the limit.
25 |
26 | Like most DNS records; TXT records are limited to 255 chars per line meaning if you attempt to juggle and manage SPF yourself, you not only have to count hostname lookups but the length of each line in your TXT record.
27 |
28 |
29 | With DMARC being listed in Gartner's top project list in 2021, more and more organasations are protecting their brand by preventing e-mail domain spoofing that relies on SPF and DKIM. So, the requirement to exceed the SPF host lookup limit of 10 for a mid to large+ size organisation has never been greater. Expurgate makes the whole process easy, and means you don't have to juggle SPF on subdomains, deal with 255 byte/character limit per line in TXT records or worry about the 10 SPF host resolution lookup limit.
30 |
31 | # The solution
32 |
33 | ### Simplify
34 | Expurgate simplifies DNS management for SPF by using a single record with variables. This removes the chance of human error and isolates issues with loops and broken upstream SPF records.
35 |
36 |
37 |
38 |
39 | ### Hide
40 | Copy your old SPF record to unused subdomain defined in `SOURCE_PREFIX=`. Your old SPF record might look something like this:
41 |
42 | #### BEFORE
43 |
44 | "v=spf1 include:sendgrid.net include:_spf.google.com include:mailgun.org include:spf.protection.outlook.com include:_netblocks.mimecast.com -all"
45 |
46 | By using an SPF Macro in place of your old SPF record, the hostnames and IP addresses are hidden from opportunistic threat actors prying eyes that could use this information against you (e.g. Targetted phishing e-mails using sendgrid branding based on `include:sendgrid.net` being visible in your SPF records).
47 |
48 | #### AFTER
49 |
50 | "v=spf1 include:%{ir}.%{d}._spf.yourdomain.com -all"
51 |
52 | The old SPF record not only gives away the names of all the service providers you use that need to legitimately spoof your domain, but could also exceed the lookup limit.
53 |
54 | > DISCLAIMER: Security through obscurity (or security by obscurity) is the reliance in security engineering on design or implementation secrecy as the main method of providing security to a system or component. While hiding SPF records may be beneficial, anyone on the internet can still check an IP against the record and whether it receives a PASS or FAIL. Technically brute force methods could be used against an SPF macro record; or targetted checks, e.g. lookup sengrid, microsoft, mailgun IP addresses to determine if a domain uses one of these vendors (or any others) - BGP prefix data could also be used to determine which IP within an enterprises subnets can e-mail on their behalf.
55 |
56 | ### Exceed SPF Limits
57 | Expurgate resolves hostnames to IP address and subnets every `DELAY=` seconds and generates an RBLDNSD configuration file. With only 1 INCLUDE: in your new SPF record you never need to worry about exceeding the 10 lookup limit or the 255 byte/character limit per line.
58 |
59 | # How does it work?
60 |
61 | The Expurgate Resolver service collects IP addresses and subnets from your source DNS TXT record and from this, generates config files for DNS server rbldnsd.
62 |
63 | 
64 |
65 | There are two seperate services running, with a third service being optional:
66 | 1. The expurgate-resolver container is responsible for dynamically generating the rbldsnd config files
67 | 2. The expurgate-rblsdnsd container is the DNS server listening on UDP/53
68 | 3. \(OPTIONAL\) Use [dnsdist](https://dnsdist.org/) as a load balancer in front of rbldnsd to handle DDoS and support both UDP/53 + TCP/53, NOTE: All traffic to rbldnsd will appear to come from dnsdist
69 |
70 | To keep the solution lightweight, no database or frontend UI is used, although these could be added to future version. Source records are stored in another obfuscated or hidden TXT record using a subdomain. This also means when the expurgate-resolver script runs it will regenerate config files when changes are detected which rbldnsd will automatically pickup.
71 |
72 | # How do I run it?
73 |
74 | ## (OPTION 1) - Try it without any setup
75 | A live demo, is setup and running that can be used or tested. Please note this is being hosted on a single AWS Lightsail Debian instance and comes without GUARANTEE or WARRANTY.
76 | https://xpg8.ehlo.email/ ~~https://xpg8.tk~~
77 |
78 | A list of common SPF records are being hosted here, allowing you to test or switchout your records that are pushing you over with these.
79 |
80 | ## (OPTION 2) - Amazon Lightsail install script
81 |
82 | ### Step 1 - Setup your source SPF record
83 | Copy your current domains SPF record to an unused subdomain which will be set in `SOURCE_PREFIX=` e.g. _sd6sdyfn
84 |
85 | _sd6sdyfn.yourdomain.com. IN TXT "v=spf1 include:sendgrid.net include:mailgun.org -all"
86 | _sd6sdyfn.yourdomain2.com. IN TXT "v=spf1 include:mailgun.org -all"
87 | _sd6sdyfn.yourdomain3.com. IN TXT "v=spf1 ip4:192.0.2.1 include:email.freshdesk.com include:sendgrid.net ~all"
88 |
89 |
90 | ### Step 2 - Amazon Lightsail install script
91 | Run the below, as a launch script to simplify the configuration:
92 |
93 | ````
94 | wget https://raw.githubusercontent.com/smck83/expurgate/main/install.sh && chmod 755 install.sh && ./install.sh && \
95 | docker run -d -v /opt/expurgate/:/spf-resolver/output/ -e DELAY=300 -e MY_DOMAINS='yourdomain.com yourdomain2.com yourdomain3.com' -e SOURCE_PREFIX='_sd6sdyfn' --dns 1.1.1.1 --dns 8.8.8.8 smck83/expurgate-resolver && \
96 | docker run -d -p 53:53/udp -v /opt/expurgate/:/var/lib/rbldnsd/:ro -e OPTIONS='-e -t 5m -l -' -e TYPE=combined -e ZONE=_spf.yourdomain.com smck83/expurgate-rbldnsd
97 |
98 | ````
99 | Set a static IP for your Lightsail instance, and open UDP port: 53.
100 |
101 | ### Step 3 - Create A + NS records
102 | 1) Create an A record e.g. spf-ns.yourdomain.com and point it to the AWS Lightsail public IP that will be hosting your expurgate-rbldnsd container on UDP/53 -
103 | spf-ns.yourdomain.com. IN A 192.0.2.1
104 |
105 | 2) Then point your NS records of _spf.yourdomain.com to the A record, this will be what you set for `ZONE=` for expurgate-rbldnsd e.g.
106 |
107 | _spf.yourdomain.com. IN NS spf-ns.yourdomain.com
108 |
109 |
110 | ### Step 4 - Replace your old SPF record with a macro pointing to expurgate-rbldsnd
111 | NOTE: Many domains (e.g. yourdomain.com, yourdomain2.com and yourdomain3.com) should all point to the same location below, i.e. in a single deployment there is a single `_spf.yourdomain.com` rbldnsd name server:
112 |
113 | "v=spf1 include:%{ir}.%{d}._spf.yourdomain.com -all"
114 |
115 | ## (OPTION 3) - End to end configuration
116 | For Step 3 & 4 use CLI or [Docker-compose.yaml](https://github.com/smck83/expurgate/blob/main/docker-compose.yaml)
117 |
118 | ### Step 1 - Create A + NS records
119 | 1)Create an A record e.g. spf-ns.yourdomain.com and point it to the public IP that will be hosting your expurgate-rbldnsd container on UDP/53 - you may wish to use [dnsdist](https://dnsdist.org/) in front of RBLDNSD to serve both TCP and UDP but also deal with DDoS.
120 |
121 | spf-ns.yourdomain.com. IN A 192.0.2.1
122 |
123 | 2)Then point your NS records of _spf.yourdomain.com to the A record, this will be what you set for `ZONE=` for expurgate-rbldnsd e.g.
124 |
125 | _spf.yourdomain.com. IN NS spf-ns.yourdomain.com
126 |
127 | ### Step 2 - Setup your source SPF record
128 | Copy your current domains SPF record to an unused subdomain which will be set in `SOURCE_PREFIX=` e.g. _sd6sdyfn
129 |
130 | _sd6sdyfn.yourdomain.com. IN TXT "v=spf1 include:sendgrid.net include:mailgun.org -all"
131 | _sd6sdyfn.yourdomain2.com. IN TXT "v=spf1 include:mailgun.org -all"
132 | _sd6sdyfn.yourdomain3.com. IN TXT "v=spf1 ip4:192.0.2.1 include:email.freshdesk.com include:sendgrid.net ~all"
133 |
134 | ### Step 3 - Run the expurgate-resolver first, so your RBLDNSD config is ready for the next step
135 | docker run -t -v /xpg8/rbldnsd-configs:/spf-resolver/output -e DELAY=300 -e MY_DOMAINS="yourdomain.com yourdomain2.com yourdomain3.com" -e RUNNING_CONFIG_ON=1 -e SOURCE_PREFIX="_sd6sdyfn" --dns 1.1.1.1 --dns 8.8.8.8 smck83/expurgate-resolver
136 | NOTE: It would be recommended to use a [local DNS recursor](https://doc.powerdns.com/recursor/) instead of public ones like 1.1.1.1 or 8.8.8.8 - particularly if you have a large volume of domains.
137 | ### Step 4 - Run expurgate-rbldnsd
138 | docker run -t -p 53:53/udp -v /xpg8/rbldnsd-configs:/var/lib/rbldnsd/:ro -e OPTIONS='-e -t 5m -l -' -e TYPE=combined -e ZONE=_spf.yourdomain.com smck83/expurgate-rbldnsd
139 | ### Step 5 - Replace your old SPF record with a macro pointing to expurgate-rbldsnd
140 | "v=spf1 include:%{ir}.%{d}._spf.yourdomain.com -all"
141 |
142 | # Environment Variables
143 | | Container | Variable | Description | Required? |
144 | | ------------- | ------------- | ------------- | ------------- |
145 | | expurgate-resolver | DELAY | This is the delay in seconds between running the script to generate new RBLDNSD config files for RBLDNSD to pickup. `DEFAULT: 300` | N |
146 | | expurgate-resolver | MY_DOMAINS | A list of domains seperated by a space that you want config files to be generated for. Example: `yourdomain.com microsoft.com github.com` | Y^ |
147 | | expurgate-resolver | SOURCE_PREFIX | This is where you will publish your 'hidden' SPF record; the source of truth e.g. you might host it at _sd3fdsfd.yourdomain.com( so will be SOURCE_PREFIX=_sd3fdsfd) Default: `_xpg8` | N |
148 | | expurgate-resolver | SOURCE_PREFIX_OFF | Only change for testing DEFAULT: `False` | N |
149 | | expurgate-resolver | UPTIMEKUMA_PUSH_URL | Monitor expurgate-resolver health (uptime and time per loop) with an [Uptime Kuma](https://github.com/louislam/uptime-kuma) 'push' monitor. URL should end in ping= Example: `https://status.yourdomain.com/api/push/D0A90al0HA?status=up&msg=OK&ping=` | N |
150 | | expurgate-resolver | RUNNING_CONFIG_ON | When set to: `1`, resolver will generate a single conf file called `running-config` for all domains in `MY_DOMAINS`, instead of one config file per domain. The main benefit is expurgate-rbldnsd doesnt need to be restarted to learn about new files and deleted domains. Default is on `RUNNING_CONFIG_ON=1` | N |
151 | | expurgate-resolver | TZ | Set the timezone [more here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)| N |
152 | | expurgate-rbldnsd | OPTIONS | These are rbldnsd run [options - more here](https://linux.die.net/man/8/rbldnsd) Recommend: `-e -t 5m -l -`
`-e` = Allow non-network addresses to be used in CIDR ranges.
`-t 5m` = Set TTL
`l -` = Set Logfile to standard output | Y |
153 | | expurgate-rbldnsd | TYPE | These are rbldnsd zone types [options - more here](https://linux.die.net/man/8/rbldnsd) Recommend: `combined` | Y |
154 | | expurgate-rbldnsd | ZONE | The last part of your SPF record (where rbldnsd is hosted), from step 1(2) EXAMPLE: `_spf.yourdomain.com` | Y |
155 |
156 | ^ If left blank `SOURCE_PREFIX_OFF` will be set to true and container will run in demo mode using microsoft.com, mimecast.com and google.com
157 |
158 | NOTE: Because one container is generating config files for the other container, it is IMPORTANT that both containers have their respective volumes mapped to the same path e.g. /xpg8/rbldnsd-config
159 |
160 | # Sample Requests & Responses
161 | ## An SPF pass checking 66.249.80.1 - [Test here](https://ehlo.email/?domain=1.80.249.66._spf.google.com.s.ehlo.email#spf)
162 |
163 | Suppose an e-mail was sent using the ENVELOPE FROM: domain _spf.google.com from the IPv4 address `66.249.80.1`
164 | The recieving e-mail server will respond to the macro in your domains SPF record and interpret the below:
165 |
166 | ${ir} - the sending servers IP address in reverse. So `66.249.80.1` will be `1.80.249.66`
167 |
168 | ${d} - the sending servers domain name (in `ENVELOPE FROM:` field) is `_spf.google.com`
169 |
170 | The request:
171 |
172 | 1.80.249.66._spf.google.com.s.ehlo.email
173 |
174 | The response from expurgate-rbldnsd:
175 |
176 | 1.80.249.66._spf.google.com.s.ehlo.email. 300 IN TXT "v=spf1 ip4:66.249.80.1 -all"
177 |
178 |
179 | NOTE(above): The response only includes the IP checked, and not every other vendor or provider in your `{SOURCE_PREFIX}.yourdomain.com` DNS TXT record.
180 |
181 | ## An SPF pass checking 2607:f8b0:4000:0000:0000:0000:0000:0001 - [Test here](https://ehlo.email/?domain=1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.b.8.f.7.0.6.2._spf.google.com.s.ehlo.email&ref=1.80.249.66._spf.google.com.s.ehlo.email#spf)
182 |
183 | Suppose an e-mail was sent using the ENVELOPE FROM: domain ehlo.email from the IPv6 address `2607:f8b0:4000:0000:0000:0000:0000:0001`
184 | The recieving e-mail server will respond to the macro in your domains SPF record and interpret the below:
185 |
186 | ${ir} - the sending servers IP address in reverse. So `2607:f8b0:4000:0000:0000:0000:0000:0001` will be reversed in dotted notation `1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.b.8.f.7.0.6.2`
187 |
188 | ${d} - the sending servers domain name (in ENVELOPE FROM: field) is `ehlo.email`
189 |
190 | The request:
191 |
192 | 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.b.8.f.7.0.6.2._spf.google.com.s.ehlo.email
193 |
194 | The response from expurgate-rbldnsd:
195 |
196 | 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.b.8.f.7.0.6.2._spf.google.com.s.ehlo.email. 300 IN TXT "v=spf1 ip6:2607:f8b0:4000::1 ~all"
197 |
198 | ## An SPF fail checking 127.0.0.1 - [Test here](https://ehlo.email/?domain=1.0.0.127._spf.google.com.s.ehlo.email&ref=1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.b.8.f.7.0.6.2._spf.google.com.s.ehlo.email#spf)
199 |
200 | ${ir} - the sending servers IP address in reverse. So `127.0.0.1` will be `1.0.0.127`
201 |
202 | ${d} - the sending servers domain name (in ENVELOPE FROM: field) is `_spf.google.com`
203 |
204 | The request:
205 |
206 | 1.0.0.127._spf.google.com.s.ehlo.email.
207 |
208 | The response from expurgate-rbldnsd:
209 |
210 | 1.0.0.127._spf.google.com.s.ehlo.email. 300 IN TXT "v=spf1 -all"
211 |
212 |
213 |
214 | # Cloud hosted SPF solutions
215 | There are a number of vendors that offer SPF management capability. However I could not find any self-hosted options. Common terms for these services are SPF flattening and SPF compression.
216 |
217 | # Performance testing
218 | My testing has proven performance with over 570 domains in `MY_DOMAINS`, running for 38 days; average total resolution and file generation times are ~2 minutes for python vs < 1 minute when running resolver with pypy. For this reason; all docker containers are using pypy.
219 |
220 | 
221 |
222 | # Recent enhancements
223 | - Running config is on by default and is recommended. The benefit of a single `running-config` file versus 1 file per domain is that when domains are added and removed no file level cleanup or service restart of rbldnsd is required.
224 | - Improved reliability: MY_DOMAINS must have a valid DNS response, be a TXT record and have a record starting with '\"v=spf1 ' or no config files will be written to disk, until resolved (19/06/2023).
225 | - pypy : Docker image is using pypy to run the Expurgate Resolver script. This increases performance of DNS record generation by 2-5x's (19/03/2023) `UPDATE(24-Oct-23); pypy is more memory intensive than python3. It has been observed after running on a low spec machine (e.g. AWS lighstail $3.50/month) for several days the resolver script stops without error, and restarts.`
226 | - AAAA Support: References to hostnames via A\A: or MX\MX: now perform a AAAA lookup to handle ip6 addresses.
227 | - Expurgate Solo : an updated version where both rbldnsd and resolver are in a single docker container using supervisord https://github.com/smck83/expurgate-solo/
228 | - Dedupe : If record already exists in 'list', do not add it again
229 | - Write2Disk on Change: Instead of regenerating config files every time the script runs, the rbldnsd config will only be written should a record change since last run. A python dictionary is used to track this, however if scale is required REDIS or something similiar could be used.
230 | - RestDB: RestDB capability has been added to manage MY_DOMAINS from restDB instead of via ENV.
231 | - Running Config : Running config means a single rbldnsd config file is generated for ALL domains which means the expurgate-rbldnsd container doesnt need to restart if domains are added or removed from MY_DOMAINS or in RestDB
232 | - Cache added : Given many INCLUDE: tend to be the same per source, e.g. mailgun.org, sendgrid.net, \_spf.google.com etc. A python disctionary has been added called dnsCache. If the record has already been looked up by another domain the response the second or third+ time will come from memory, saving a DNS request.
233 |
234 | # Buy me a coffee
235 | If this was useful, feel free to [Buy me a coffee](https://www.buymeacoffee.com/smc83)
236 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | services:
3 | spf-resolver:
4 | image: smck83/expurgate-resolver
5 | environment:
6 | DELAY: '300'
7 | MY_DOMAINS: yourdomain.com yourdomain2.com yourdomain3.com
8 | SOURCE_PREFIX: _sd6sdyfn
9 | RUNNING_CONFIG_ON: 1
10 | stdin_open: true
11 | restart: always
12 | volumes:
13 | - /xpg8/rbldnsd-configs:/spf-resolver/output
14 | dns:
15 | - 1.1.1.1
16 | - 8.8.8.8
17 | tty: true
18 | spf-rbldnsd:
19 | image: smck83/expurgate-rbldnsd
20 | environment:
21 | OPTIONS: -e -t 5m -l -
22 | TYPE: combined
23 | ZONE: _spf.yourdomain.com
24 | stdin_open: true
25 | restart: always
26 | volumes:
27 | - /xpg8/rbldnsd-configs:/var/lib/rbldnsd/:ro
28 | tty: true
29 | ports:
30 | - 53:53/udp
31 |
--------------------------------------------------------------------------------
/expurgate-diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smck83/expurgate/da971d2068489f50ac3b6eba410145824f804e7c/expurgate-diagram.png
--------------------------------------------------------------------------------
/expurgate-rbldnsd/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:stable
2 | LABEL maintainer="s@mck.la"
3 |
4 | RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
5 | rbldnsd ntp dnsutils\
6 | && rm -rf /var/lib/apt/lists/* /var/cache/apt/*
7 |
8 | COPY running-config /var/lib/rbldnsd/running-config
9 | VOLUME ["/var/lib/rbldnsd"]
10 | WORKDIR /var/lib/rbldnsd
11 |
12 | ENTRYPOINT /usr/sbin/rbldnsd -b 0.0.0.0 -n ${OPTIONS} ${ZONE}:${TYPE}:$(ls /var/lib/rbldnsd/| grep - | tr -s '\n' ',' | rev | cut -c 2- | rev)
13 |
14 | EXPOSE 53/udp
15 |
--------------------------------------------------------------------------------
/expurgate-rbldnsd/running-config:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.ehlo.email | https://github.com/smck83/expurgate
12 | # DEFAULT CONFIG
13 | $DATASET ip4set:xpg8.local xpg8.local
14 | :3:v=spf1 ip4:$ ~all
15 | 192.0.2.1 # non-routable sample ip
16 | 127.0.0.2 # non-routable sample ip
17 | :99:v=spf1 ~all
18 | 0.0.0.0/1 # all other IPv4 addresses
19 | 128.0.0.0/1 # all other IP IPv4 addresses
20 | $DATASET ip6trie:xpg8.local xpg8.local
21 | :3:v=spf1 ip6:$ ~all
22 | :99:v=spf1 ~all
23 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
24 |
--------------------------------------------------------------------------------
/expurgate-resolver/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11
2 | #FROM pypy:3.9
3 | LABEL maintainer="expurgate@mck.la"
4 | RUN pip install dnspython requests jsonpath-ng \
5 | && mkdir -p /spf-resolver/output
6 | WORKDIR /spf-resolver
7 | COPY resolver.py /spf-resolver/resolver.py
8 |
9 |
10 | CMD ["python3","./resolver.py"]
11 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/_netblocks-mimecast-com:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:_netblocks.mimecast.com @ 2023-05-24 20:54:45.956853
13 | # Source of truth: _netblocks.mimecast.com - Will not work in production unless you replace a single record. e.g. include:_netblocks.mimecast.com with include:%{ir}._netblocks.mimecast.com._spf.yourdomain.com
14 | # ^ _netblocks.mimecast.com
15 | # ^ v=spf1 include:eu._netblocks.mimecast.com include:us._netblocks.mimecast.com include:za._netblocks.mimecast.com include:de._netblocks.mimecast.com include:au._netblocks.mimecast.com ~all
16 | # ^^ eu._netblocks.mimecast.com
17 | # ^^ v=spf1 ip4:195.130.217.0/24 ip4:91.220.42.0/24 ip4:146.101.78.0/24 ip4:207.82.80.0/24 ip4:213.167.81.0/25 ip4:193.7.207.0/25 ip4:213.167.75.0/25 ip4:185.58.85.0/24 ip4:185.58.86.0/24 ip4:193.7.206.0/25 ip4:147.28.36.0/24 ~all
18 | # ^^^ us._netblocks.mimecast.com
19 | # ^^^ v=spf1 ip4:207.211.31.0/25 ip4:205.139.110.0/24 ip4:216.205.24.0/24 ip4:170.10.129.0/24 ip4:63.128.21.0/24 ip4:170.10.133.0/24 ip4:185.58.84.93/32 ip4:207.211.41.113/32 ip4:207.211.30.64/26 ip4:207.211.30.128/25 ip4:216.145.221.0/24 ip4:170.10.128.0/24 ~all
20 | # ^^^^ za._netblocks.mimecast.com
21 | # ^^^^ v=spf1 ip4:41.74.192.0/22 ip4:41.74.200.0/23 ip4:41.74.196.0/22 ip4:41.74.204.0/23 ip4:41.74.206.0/24 ~all
22 | # ^^^^^ de._netblocks.mimecast.com
23 | # ^^^^^ v=spf1 ip4:51.163.158.0/24 ip4:194.104.109.0/24 ip4:62.140.7.0/24 ip4:194.104.111.0/24 ip4:51.163.159.21/32 ip4:62.140.10.21/32 ip4:194.104.110.0/24 ~all
24 | # ^^^^^^ au._netblocks.mimecast.com
25 | # ^^^^^^ v=spf1 ip4:103.13.69.0/24 ip4:124.47.150.0/24 ip4:124.47.189.0/24 ip4:103.96.23.0/24 ip4:103.96.21.0/24 ip4:180.189.28.0/24 ip4:216.145.217.0/24 ip4:103.96.22.0/24 ~all
26 | # Depth:6
27 | # IP & Subnet: 43
28 | $DATASET ip4set:_netblocks.mimecast.com _netblocks.mimecast.com
29 | :3:v=spf1 ip4:$ ~all
30 | 195.130.217.0/24 # subnet:eu._netblocks.mimecast.com
31 | 91.220.42.0/24 # subnet:eu._netblocks.mimecast.com
32 | 146.101.78.0/24 # subnet:eu._netblocks.mimecast.com
33 | 207.82.80.0/24 # subnet:eu._netblocks.mimecast.com
34 | 213.167.81.0/25 # subnet:eu._netblocks.mimecast.com
35 | 193.7.207.0/25 # subnet:eu._netblocks.mimecast.com
36 | 213.167.75.0/25 # subnet:eu._netblocks.mimecast.com
37 | 185.58.85.0/24 # subnet:eu._netblocks.mimecast.com
38 | 185.58.86.0/24 # subnet:eu._netblocks.mimecast.com
39 | 193.7.206.0/25 # subnet:eu._netblocks.mimecast.com
40 | 147.28.36.0/24 # subnet:eu._netblocks.mimecast.com
41 | 207.211.31.0/25 # subnet:us._netblocks.mimecast.com
42 | 205.139.110.0/24 # subnet:us._netblocks.mimecast.com
43 | 216.205.24.0/24 # subnet:us._netblocks.mimecast.com
44 | 170.10.129.0/24 # subnet:us._netblocks.mimecast.com
45 | 63.128.21.0/24 # subnet:us._netblocks.mimecast.com
46 | 170.10.133.0/24 # subnet:us._netblocks.mimecast.com
47 | 185.58.84.93/32 # ip:us._netblocks.mimecast.com
48 | 207.211.41.113/32 # ip:us._netblocks.mimecast.com
49 | 207.211.30.64/26 # subnet:us._netblocks.mimecast.com
50 | 207.211.30.128/25 # subnet:us._netblocks.mimecast.com
51 | 216.145.221.0/24 # subnet:us._netblocks.mimecast.com
52 | 170.10.128.0/24 # subnet:us._netblocks.mimecast.com
53 | 41.74.192.0/22 # subnet:za._netblocks.mimecast.com
54 | 41.74.200.0/23 # subnet:za._netblocks.mimecast.com
55 | 41.74.196.0/22 # subnet:za._netblocks.mimecast.com
56 | 41.74.204.0/23 # subnet:za._netblocks.mimecast.com
57 | 41.74.206.0/24 # subnet:za._netblocks.mimecast.com
58 | 51.163.158.0/24 # subnet:de._netblocks.mimecast.com
59 | 194.104.109.0/24 # subnet:de._netblocks.mimecast.com
60 | 62.140.7.0/24 # subnet:de._netblocks.mimecast.com
61 | 194.104.111.0/24 # subnet:de._netblocks.mimecast.com
62 | 51.163.159.21/32 # ip:de._netblocks.mimecast.com
63 | 62.140.10.21/32 # ip:de._netblocks.mimecast.com
64 | 194.104.110.0/24 # subnet:de._netblocks.mimecast.com
65 | 103.13.69.0/24 # subnet:au._netblocks.mimecast.com
66 | 124.47.150.0/24 # subnet:au._netblocks.mimecast.com
67 | 124.47.189.0/24 # subnet:au._netblocks.mimecast.com
68 | 103.96.23.0/24 # subnet:au._netblocks.mimecast.com
69 | 103.96.21.0/24 # subnet:au._netblocks.mimecast.com
70 | 180.189.28.0/24 # subnet:au._netblocks.mimecast.com
71 | 216.145.217.0/24 # subnet:au._netblocks.mimecast.com
72 | 103.96.22.0/24 # subnet:au._netblocks.mimecast.com
73 | :99:v=spf1 ~all
74 | 0.0.0.0/1 # all other IPv4 addresses
75 | 128.0.0.0/1 # all other IP IPv4 addresses
76 | $DATASET ip6trie:_netblocks.mimecast.com _netblocks.mimecast.com
77 | :3:v=spf1 ip6:$ ~all
78 | :99:v=spf1 ~all
79 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
80 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/_spf-google-com:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:_spf.google.com @ 2023-05-24 20:54:45.861588
13 | # Source of truth: _spf.google.com - Will not work in production unless you replace a single record. e.g. include:_spf.google.com with include:%{ir}._spf.google.com._spf.yourdomain.com
14 | # ^ _spf.google.com
15 | # ^ v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all
16 | # ^^ _netblocks.google.com
17 | # ^^ v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all
18 | # ^^^ _netblocks2.google.com
19 | # ^^^ v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all
20 | # ^^^^ _netblocks3.google.com
21 | # ^^^^ v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:172.253.56.0/21 ip4:172.253.112.0/20 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all
22 | # Depth:4
23 | # IP & Subnet: 27
24 | $DATASET ip4set:_spf.google.com _spf.google.com
25 | :3:v=spf1 ip4:$ ~all
26 | 35.190.247.0/24 # subnet:_netblocks.google.com
27 | 64.233.160.0/19 # subnet:_netblocks.google.com
28 | 66.102.0.0/20 # subnet:_netblocks.google.com
29 | 66.249.80.0/20 # subnet:_netblocks.google.com
30 | 72.14.192.0/18 # subnet:_netblocks.google.com
31 | 74.125.0.0/16 # subnet:_netblocks.google.com
32 | 108.177.8.0/21 # subnet:_netblocks.google.com
33 | 173.194.0.0/16 # subnet:_netblocks.google.com
34 | 209.85.128.0/17 # subnet:_netblocks.google.com
35 | 216.58.192.0/19 # subnet:_netblocks.google.com
36 | 216.239.32.0/19 # subnet:_netblocks.google.com
37 | 172.217.0.0/19 # subnet:_netblocks3.google.com
38 | 172.217.32.0/20 # subnet:_netblocks3.google.com
39 | 172.217.128.0/19 # subnet:_netblocks3.google.com
40 | 172.217.160.0/20 # subnet:_netblocks3.google.com
41 | 172.217.192.0/19 # subnet:_netblocks3.google.com
42 | 172.253.56.0/21 # subnet:_netblocks3.google.com
43 | 172.253.112.0/20 # subnet:_netblocks3.google.com
44 | 108.177.96.0/19 # subnet:_netblocks3.google.com
45 | 35.191.0.0/16 # subnet:_netblocks3.google.com
46 | 130.211.0.0/22 # subnet:_netblocks3.google.com
47 | :99:v=spf1 ~all
48 | 0.0.0.0/1 # all other IPv4 addresses
49 | 128.0.0.0/1 # all other IP IPv4 addresses
50 | $DATASET ip6trie:_spf.google.com _spf.google.com
51 | :3:v=spf1 ip6:$ ~all
52 | 2001:4860:4000::/36 # _netblocks2.google.com
53 | 2404:6800:4000::/36 # _netblocks2.google.com
54 | 2607:f8b0:4000::/36 # _netblocks2.google.com
55 | 2800:3f0:4000::/36 # _netblocks2.google.com
56 | 2a00:1450:4000::/36 # _netblocks2.google.com
57 | 2c0f:fb50:4000::/36 # _netblocks2.google.com
58 | :99:v=spf1 ~all
59 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
60 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/google-xpg8-tk:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:google.xpg8.tk @ 2023-05-24 20:54:45.624993
13 | # Source of truth: google.xpg8.tk - Will not work in production unless you replace a single record. e.g. include:google.xpg8.tk with include:%{ir}.google.xpg8.tk._spf.yourdomain.com
14 | # ^ google.xpg8.tk
15 | # ^ v=spf1 mx:google.xpg8.tk mx:google1.xpg8.tk -all
16 | # ^^ mx:google.xpg8.tk
17 | # ^^ mx:google.xpg8.tk=>a:alt1.aspmx.l.google.com.
18 | # ^^ mx:google.xpg8.tk=>aaaa:alt1.aspmx.l.google.com.
19 | # ^^ mx:google.xpg8.tk=>a:alt2.aspmx.l.google.com.
20 | # ^^ mx:google.xpg8.tk=>aaaa:alt2.aspmx.l.google.com.
21 | # ^^ mx:google.xpg8.tk=>a:alt3.aspmx.l.google.com.
22 | # ^^ mx:google.xpg8.tk=>aaaa:alt3.aspmx.l.google.com.
23 | # ^^ mx:google.xpg8.tk=>a:alt4.aspmx.l.google.com.
24 | # ^^ mx:google.xpg8.tk=>aaaa:alt4.aspmx.l.google.com.
25 | # ^^ mx:google.xpg8.tk=>a:aspmx.l.google.com.
26 | # ^^ mx:google.xpg8.tk=>aaaa:aspmx.l.google.com.
27 | # ^^^ mx:google1.xpg8.tk
28 | # ^^^ mx:google1.xpg8.tk=>a:alt3.aspmx.l.google.com.
29 | # ^^^ mx:google1.xpg8.tk=>aaaa:alt3.aspmx.l.google.com.
30 | # Depth:3
31 | # IP & Subnet: 12
32 | $DATASET ip4set:google.xpg8.tk google.xpg8.tk
33 | :3:v=spf1 ip4:$ -all
34 | 173.194.202.26 # mx:google.xpg8.tk=>a:alt1.aspmx.l.google.com.
35 | 142.250.141.26 # mx:google.xpg8.tk=>a:alt2.aspmx.l.google.com.
36 | 142.250.115.26 # mx:google.xpg8.tk=>a:alt3.aspmx.l.google.com.
37 | 64.233.171.26 # mx:google.xpg8.tk=>a:alt4.aspmx.l.google.com.
38 | 74.125.200.27 # mx:google.xpg8.tk=>a:aspmx.l.google.com.
39 | 142.250.115.26 # mx:google1.xpg8.tk=>a:alt3.aspmx.l.google.com.
40 | :99:v=spf1 -all
41 | 0.0.0.0/1 # all other IPv4 addresses
42 | 128.0.0.0/1 # all other IP IPv4 addresses
43 | $DATASET ip6trie:google.xpg8.tk google.xpg8.tk
44 | :3:v=spf1 ip6:$ -all
45 | 2607:f8b0:400e:c00::1b # mx:google.xpg8.tk=>aaaa:alt1.aspmx.l.google.com.
46 | 2607:f8b0:4023:c0b::1b # mx:google.xpg8.tk=>aaaa:alt2.aspmx.l.google.com.
47 | 2607:f8b0:4023:1004::1b # mx:google.xpg8.tk=>aaaa:alt3.aspmx.l.google.com.
48 | 2607:f8b0:4003:c15::1a # mx:google.xpg8.tk=>aaaa:alt4.aspmx.l.google.com.
49 | 2404:6800:4003:c01::1b # mx:google.xpg8.tk=>aaaa:aspmx.l.google.com.
50 | 2607:f8b0:4023:1004::1b # mx:google1.xpg8.tk=>aaaa:alt3.aspmx.l.google.com.
51 | :99:v=spf1 -all
52 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
53 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/mailgun-org:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:mailgun.org @ 2023-05-24 20:54:47.737343
13 | # Source of truth: mailgun.org - Will not work in production unless you replace a single record. e.g. include:mailgun.org with include:%{ir}.mailgun.org._spf.yourdomain.com
14 | # ^ mailgun.org
15 | # ^ v=spf1 include:_spf.mailgun.org include:_spf.eu.mailgun.org -all
16 | # ^^ _spf.mailgun.org
17 | # ^^ v=spf1 ip4:209.61.151.0/24 ip4:166.78.68.0/22 ip4:198.61.254.0/23 ip4:192.237.158.0/23 ip4:23.253.182.0/23 ip4:104.130.96.0/28 ip4:146.20.113.0/24 ip4:146.20.191.0/24 ip4:159.135.224.0/20 ip4:69.72.32.0/20 ip4:104.130.122.0/23 ip4:146.20.112.0/26 ip4:161.38.192.0/20 ip4:143.55.224.0/21 ip4:143.55.232.0/22 ip4:159.112.240.0/20 ip4:198.244.48.0/20 ~all
18 | # ^^^ _spf.eu.mailgun.org
19 | # ^^^ v=spf1 ip4:141.193.32.0/23 ip4:159.135.140.80/29 ip4:159.135.132.128/25 ip4:161.38.204.0/22 ip4:87.253.232.0/21 ip4:185.189.236.0/22 ip4:185.211.120.0/22 ip4:185.250.236.0/22 ip4:143.55.236.0/22 ip4:198.244.60.0/22 ~all
20 | # Depth:3
21 | # IP & Subnet: 27
22 | $DATASET ip4set:mailgun.org mailgun.org
23 | :3:v=spf1 ip4:$ ~all
24 | 209.61.151.0/24 # subnet:_spf.mailgun.org
25 | 166.78.68.0/22 # subnet:_spf.mailgun.org
26 | 198.61.254.0/23 # subnet:_spf.mailgun.org
27 | 192.237.158.0/23 # subnet:_spf.mailgun.org
28 | 23.253.182.0/23 # subnet:_spf.mailgun.org
29 | 104.130.96.0/28 # subnet:_spf.mailgun.org
30 | 146.20.113.0/24 # subnet:_spf.mailgun.org
31 | 146.20.191.0/24 # subnet:_spf.mailgun.org
32 | 159.135.224.0/20 # subnet:_spf.mailgun.org
33 | 69.72.32.0/20 # subnet:_spf.mailgun.org
34 | 104.130.122.0/23 # subnet:_spf.mailgun.org
35 | 146.20.112.0/26 # subnet:_spf.mailgun.org
36 | 161.38.192.0/20 # subnet:_spf.mailgun.org
37 | 143.55.224.0/21 # subnet:_spf.mailgun.org
38 | 143.55.232.0/22 # subnet:_spf.mailgun.org
39 | 159.112.240.0/20 # subnet:_spf.mailgun.org
40 | 198.244.48.0/20 # subnet:_spf.mailgun.org
41 | 141.193.32.0/23 # subnet:_spf.eu.mailgun.org
42 | 159.135.140.80/29 # subnet:_spf.eu.mailgun.org
43 | 159.135.132.128/25 # subnet:_spf.eu.mailgun.org
44 | 161.38.204.0/22 # subnet:_spf.eu.mailgun.org
45 | 87.253.232.0/21 # subnet:_spf.eu.mailgun.org
46 | 185.189.236.0/22 # subnet:_spf.eu.mailgun.org
47 | 185.211.120.0/22 # subnet:_spf.eu.mailgun.org
48 | 185.250.236.0/22 # subnet:_spf.eu.mailgun.org
49 | 143.55.236.0/22 # subnet:_spf.eu.mailgun.org
50 | 198.244.60.0/22 # subnet:_spf.eu.mailgun.org
51 | :99:v=spf1 ~all
52 | 0.0.0.0/1 # all other IPv4 addresses
53 | 128.0.0.0/1 # all other IP IPv4 addresses
54 | $DATASET ip6trie:mailgun.org mailgun.org
55 | :3:v=spf1 ip6:$ ~all
56 | :99:v=spf1 ~all
57 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
58 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/outbound-mailhop-org:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:outbound.mailhop.org @ 2023-05-24 20:54:47.429184
13 | # Source of truth: outbound.mailhop.org - Will not work in production unless you replace a single record. e.g. include:outbound.mailhop.org with include:%{ir}.outbound.mailhop.org._spf.yourdomain.com
14 | # ^ outbound.mailhop.org
15 | # ^ v=spf1 ip4:52.26.49.97 ip4:52.10.99.51 ip4:52.28.59.28 ip4:52.28.6.212 ip4:3.127.8.113 ip4:54.186.27.61 ip4:52.28.168.19 ip4:52.58.19.153 ip4:52.58.96.151 ip4:52.58.97.209 ip4:52.88.73.233 ip4:54.187.71.48 ip4:54.149.36.10 ip4:54.68.34.165 ip4:54.186.22.84 ip4:54.191.214.3 ip4:3.125.66.160 ip4:54.213.22.21 ip4:52.29.21.168 ip4:100.20.105.3 ip4:44.233.67.66 ip4:54.185.97.28 include:_s0.outbound.mailhop.org -all
16 | # ^^ _s0.outbound.mailhop.org
17 | # ^^ v=spf1 ip4:54.190.72.79 ip4:18.156.67.42 ip4:54.69.130.42 ip4:3.124.88.253 ip4:52.29.156.81 ip4:52.28.246.64 ip4:54.68.138.64 ip4:3.121.156.226 ip4:54.191.214.36 ip4:44.233.38.249 ip4:44.233.99.129 ip4:54.149.240.58 ip4:18.156.94.234 ip4:54.186.10.118 ip4:3.125.148.246 ip4:54.187.63.214 ip4:54.149.35.133 ip4:52.10.130.167 ip4:54.148.38.162 ip4:54.187.206.49 ip4:44.233.143.45 include:_s1.outbound.mailhop.org -all
18 | # ^^^ _s1.outbound.mailhop.org
19 | # ^^^ v=spf1 ip4:35.157.29.171 ip4:54.191.158.99 ip4:54.186.172.23 ip4:54.186.57.195 ip4:54.148.219.64 ip4:54.148.153.48 ip4:54.148.30.215 ip4:54.148.222.11 ip4:54.186.218.12 ip4:52.28.251.132 ip4:52.28.147.211 ip4:52.58.109.202 ip4:54.187.218.212 ip4:54.187.213.119 ip4:54.149.210.130 ip4:54.148.165.188 ip4:54.149.155.156 ip4:54.148.113.140 ip4:35.156.234.212 ip4:54.149.205.143 include:_s2.outbound.mailhop.org -all
20 | # ^^^^ _s2.outbound.mailhop.org
21 | # ^^^^ v=spf1 ip4:54.149.206.185 ip4:54.244.192.240 ip4:54.191.151.194 ip4:54.200.247.200 ip4:54.200.129.228 ip4:35.160.111.225 ip4:23.83.208.0/20 ip4:191.252.57.0/25 ip4:34.219.98.66/32 ip4:52.34.31.144/32 ip4:46.232.183.0/24 ip4:104.232.45.0/24 ip4:199.10.31.235/32 ip4:54.245.125.39/32 ip4:172.255.62.10/32 ip4:172.255.62.11/32 ip4:177.153.0.128/25 ip4:177.153.0.130/32 ip4:199.10.31.236/32 ip4:54.214.232.113/32 include:_s3.outbound.mailhop.org -all
22 | # ^^^^^ _s3.outbound.mailhop.org
23 | # ^^^^^ v=spf1 ip4:103.18.109.138/32 -all
24 | # Depth:5
25 | # IP & Subnet: 84
26 | $DATASET ip4set:outbound.mailhop.org outbound.mailhop.org
27 | :3:v=spf1 ip4:$ -all
28 | 52.26.49.97 # ip:outbound.mailhop.org
29 | 52.10.99.51 # ip:outbound.mailhop.org
30 | 52.28.59.28 # ip:outbound.mailhop.org
31 | 52.28.6.212 # ip:outbound.mailhop.org
32 | 3.127.8.113 # ip:outbound.mailhop.org
33 | 54.186.27.61 # ip:outbound.mailhop.org
34 | 52.28.168.19 # ip:outbound.mailhop.org
35 | 52.58.19.153 # ip:outbound.mailhop.org
36 | 52.58.96.151 # ip:outbound.mailhop.org
37 | 52.58.97.209 # ip:outbound.mailhop.org
38 | 52.88.73.233 # ip:outbound.mailhop.org
39 | 54.187.71.48 # ip:outbound.mailhop.org
40 | 54.149.36.10 # ip:outbound.mailhop.org
41 | 54.68.34.165 # ip:outbound.mailhop.org
42 | 54.186.22.84 # ip:outbound.mailhop.org
43 | 54.191.214.3 # ip:outbound.mailhop.org
44 | 3.125.66.160 # ip:outbound.mailhop.org
45 | 54.213.22.21 # ip:outbound.mailhop.org
46 | 52.29.21.168 # ip:outbound.mailhop.org
47 | 100.20.105.3 # ip:outbound.mailhop.org
48 | 44.233.67.66 # ip:outbound.mailhop.org
49 | 54.185.97.28 # ip:outbound.mailhop.org
50 | 54.190.72.79 # ip:_s0.outbound.mailhop.org
51 | 18.156.67.42 # ip:_s0.outbound.mailhop.org
52 | 54.69.130.42 # ip:_s0.outbound.mailhop.org
53 | 3.124.88.253 # ip:_s0.outbound.mailhop.org
54 | 52.29.156.81 # ip:_s0.outbound.mailhop.org
55 | 52.28.246.64 # ip:_s0.outbound.mailhop.org
56 | 54.68.138.64 # ip:_s0.outbound.mailhop.org
57 | 3.121.156.226 # ip:_s0.outbound.mailhop.org
58 | 54.191.214.36 # ip:_s0.outbound.mailhop.org
59 | 44.233.38.249 # ip:_s0.outbound.mailhop.org
60 | 44.233.99.129 # ip:_s0.outbound.mailhop.org
61 | 54.149.240.58 # ip:_s0.outbound.mailhop.org
62 | 18.156.94.234 # ip:_s0.outbound.mailhop.org
63 | 54.186.10.118 # ip:_s0.outbound.mailhop.org
64 | 3.125.148.246 # ip:_s0.outbound.mailhop.org
65 | 54.187.63.214 # ip:_s0.outbound.mailhop.org
66 | 54.149.35.133 # ip:_s0.outbound.mailhop.org
67 | 52.10.130.167 # ip:_s0.outbound.mailhop.org
68 | 54.148.38.162 # ip:_s0.outbound.mailhop.org
69 | 54.187.206.49 # ip:_s0.outbound.mailhop.org
70 | 44.233.143.45 # ip:_s0.outbound.mailhop.org
71 | 35.157.29.171 # ip:_s1.outbound.mailhop.org
72 | 54.191.158.99 # ip:_s1.outbound.mailhop.org
73 | 54.186.172.23 # ip:_s1.outbound.mailhop.org
74 | 54.186.57.195 # ip:_s1.outbound.mailhop.org
75 | 54.148.219.64 # ip:_s1.outbound.mailhop.org
76 | 54.148.153.48 # ip:_s1.outbound.mailhop.org
77 | 54.148.30.215 # ip:_s1.outbound.mailhop.org
78 | 54.148.222.11 # ip:_s1.outbound.mailhop.org
79 | 54.186.218.12 # ip:_s1.outbound.mailhop.org
80 | 52.28.251.132 # ip:_s1.outbound.mailhop.org
81 | 52.28.147.211 # ip:_s1.outbound.mailhop.org
82 | 52.58.109.202 # ip:_s1.outbound.mailhop.org
83 | 54.187.218.212 # ip:_s1.outbound.mailhop.org
84 | 54.187.213.119 # ip:_s1.outbound.mailhop.org
85 | 54.149.210.130 # ip:_s1.outbound.mailhop.org
86 | 54.148.165.188 # ip:_s1.outbound.mailhop.org
87 | 54.149.155.156 # ip:_s1.outbound.mailhop.org
88 | 54.148.113.140 # ip:_s1.outbound.mailhop.org
89 | 35.156.234.212 # ip:_s1.outbound.mailhop.org
90 | 54.149.205.143 # ip:_s1.outbound.mailhop.org
91 | 54.149.206.185 # ip:_s2.outbound.mailhop.org
92 | 54.244.192.240 # ip:_s2.outbound.mailhop.org
93 | 54.191.151.194 # ip:_s2.outbound.mailhop.org
94 | 54.200.247.200 # ip:_s2.outbound.mailhop.org
95 | 54.200.129.228 # ip:_s2.outbound.mailhop.org
96 | 35.160.111.225 # ip:_s2.outbound.mailhop.org
97 | 23.83.208.0/20 # subnet:_s2.outbound.mailhop.org
98 | 191.252.57.0/25 # subnet:_s2.outbound.mailhop.org
99 | 34.219.98.66/32 # ip:_s2.outbound.mailhop.org
100 | 52.34.31.144/32 # ip:_s2.outbound.mailhop.org
101 | 46.232.183.0/24 # subnet:_s2.outbound.mailhop.org
102 | 104.232.45.0/24 # subnet:_s2.outbound.mailhop.org
103 | 199.10.31.235/32 # ip:_s2.outbound.mailhop.org
104 | 54.245.125.39/32 # ip:_s2.outbound.mailhop.org
105 | 172.255.62.10/32 # ip:_s2.outbound.mailhop.org
106 | 172.255.62.11/32 # ip:_s2.outbound.mailhop.org
107 | 177.153.0.128/25 # subnet:_s2.outbound.mailhop.org
108 | 177.153.0.130/32 # ip:_s2.outbound.mailhop.org
109 | 199.10.31.236/32 # ip:_s2.outbound.mailhop.org
110 | 54.214.232.113/32 # ip:_s2.outbound.mailhop.org
111 | 103.18.109.138/32 # ip:_s3.outbound.mailhop.org
112 | :99:v=spf1 -all
113 | 0.0.0.0/1 # all other IPv4 addresses
114 | 128.0.0.0/1 # all other IP IPv4 addresses
115 | $DATASET ip6trie:outbound.mailhop.org outbound.mailhop.org
116 | :3:v=spf1 ip6:$ -all
117 | :99:v=spf1 -all
118 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
119 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/running-config:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.ehlo.email | https://github.com/smck83/expurgate
12 | # Running config for: 8 domains
13 | # Source domains: _spf.google.com, _netblocks.mimecast.com, spf.protection.outlook.com, outbound.mailhop.org, spf.messagelabs.com, mailgun.org, sendgrid.net, service-now.com
14 | #
15 | #
16 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:_spf.google.com @ 2023-10-09 10:25:22.318574
17 | # Source of truth: _spf.google.com - Will not work in production unless you replace a single record. e.g. include:_spf.google.com with include:%{ir}._spf.google.com._spf.yourdomain.com
18 | # ^ _spf.google.com
19 | # ^ v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all
20 | # ^^ _netblocks.google.com
21 | # ^^ v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all
22 | # ^^^ _netblocks2.google.com
23 | # ^^^ v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all
24 | # ^^^^ _netblocks3.google.com
25 | # ^^^^ v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:172.253.56.0/21 ip4:172.253.112.0/20 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all
26 | # Depth:4
27 | # IP & Subnet: 27
28 | $DATASET ip4set:_spf.google.com _spf.google.com
29 | :3:v=spf1 ip4:$ ~all
30 | 108.177.96.0/19 # subnet:_netblocks3.google.com
31 | 108.177.8.0/21 # subnet:_netblocks.google.com
32 | 172.217.192.0/19 # subnet:_netblocks3.google.com
33 | 66.102.0.0/20 # subnet:_netblocks.google.com
34 | 64.233.160.0/19 # subnet:_netblocks.google.com
35 | 172.217.128.0/19 # subnet:_netblocks3.google.com
36 | 172.253.56.0/21 # subnet:_netblocks3.google.com
37 | 209.85.128.0/17 # subnet:_netblocks.google.com
38 | 216.239.32.0/19 # subnet:_netblocks.google.com
39 | 172.253.112.0/20 # subnet:_netblocks3.google.com
40 | 216.58.192.0/19 # subnet:_netblocks.google.com
41 | 172.217.160.0/20 # subnet:_netblocks3.google.com
42 | 72.14.192.0/18 # subnet:_netblocks.google.com
43 | 173.194.0.0/16 # subnet:_netblocks.google.com
44 | 35.190.247.0/24 # subnet:_netblocks.google.com
45 | 35.191.0.0/16 # subnet:_netblocks3.google.com
46 | 130.211.0.0/22 # subnet:_netblocks3.google.com
47 | 172.217.0.0/19 # subnet:_netblocks3.google.com
48 | 172.217.32.0/20 # subnet:_netblocks3.google.com
49 | 66.249.80.0/20 # subnet:_netblocks.google.com
50 | 74.125.0.0/16 # subnet:_netblocks.google.com
51 | :99:v=spf1 ~all
52 | 0.0.0.0/1 # all other IPv4 addresses
53 | 128.0.0.0/1 # all other IP IPv4 addresses
54 | $DATASET ip6trie:_spf.google.com _spf.google.com
55 | :3:v=spf1 ip6:$ ~all
56 | 2c0f:fb50:4000::/36 # _netblocks2.google.com
57 | 2a00:1450:4000::/36 # _netblocks2.google.com
58 | 2404:6800:4000::/36 # _netblocks2.google.com
59 | 2001:4860:4000::/36 # _netblocks2.google.com
60 | 2800:3f0:4000::/36 # _netblocks2.google.com
61 | 2607:f8b0:4000::/36 # _netblocks2.google.com
62 | :99:v=spf1 ~all
63 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
64 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:_netblocks.mimecast.com @ 2023-10-09 10:25:22.371795
65 | # Source of truth: _netblocks.mimecast.com - Will not work in production unless you replace a single record. e.g. include:_netblocks.mimecast.com with include:%{ir}._netblocks.mimecast.com._spf.yourdomain.com
66 | # ^ _netblocks.mimecast.com
67 | # ^ v=spf1 include:eu._netblocks.mimecast.com include:us._netblocks.mimecast.com include:za._netblocks.mimecast.com include:de._netblocks.mimecast.com include:au._netblocks.mimecast.com ~all
68 | # ^^ eu._netblocks.mimecast.com
69 | # ^^ v=spf1 ip4:195.130.217.0/24 ip4:91.220.42.0/24 ip4:146.101.78.0/24 ip4:207.82.80.0/24 ip4:213.167.81.0/25 ip4:193.7.207.0/25 ip4:213.167.75.0/25 ip4:185.58.85.0/24 ip4:185.58.86.0/24 ip4:193.7.206.0/25 ip4:147.28.36.0/24 ~all
70 | # ^^^ us._netblocks.mimecast.com
71 | # ^^^ v=spf1 ip4:207.211.31.0/25 ip4:205.139.110.0/24 ip4:216.205.24.0/24 ip4:170.10.129.0/24 ip4:63.128.21.0/24 ip4:170.10.133.0/24 ip4:185.58.84.93/32 ip4:207.211.41.113/32 ip4:207.211.30.64/26 ip4:207.211.30.128/25 ip4:216.145.221.0/24 ip4:170.10.128.0/24 ~all
72 | # ^^^^ za._netblocks.mimecast.com
73 | # ^^^^ v=spf1 ip4:41.74.192.0/22 ip4:41.74.200.0/23 ip4:41.74.196.0/22 ip4:41.74.204.0/23 ip4:41.74.206.0/24 ~all
74 | # ^^^^^ de._netblocks.mimecast.com
75 | # ^^^^^ v=spf1 ip4:51.163.158.0/24 ip4:194.104.109.0/24 ip4:194.104.111.0/24 ip4:51.163.159.21/32 ip4:194.104.110.21/32 ip4:194.104.110.240/28 ip4:62.140.10.21/32 ip4:62.140.7.0/24 ip4:194.104.108.240/29 ip4:194.104.108.21/32 ~all
76 | # ^^^^^^ au._netblocks.mimecast.com
77 | # ^^^^^^ v=spf1 ip4:103.13.69.0/24 ip4:124.47.150.0/24 ip4:124.47.189.0/24 ip4:103.96.23.0/24 ip4:103.96.21.0/24 ip4:180.189.28.0/24 ip4:216.145.217.0/24 ip4:103.96.22.96/28 ip4:103.96.22.22/32 ~all
78 | # Depth:6
79 | # IP & Subnet: 47
80 | $DATASET ip4set:_netblocks.mimecast.com _netblocks.mimecast.com
81 | :3:v=spf1 ip4:$ ~all
82 | 170.10.133.0/24 # subnet:us._netblocks.mimecast.com
83 | 41.74.204.0/23 # subnet:za._netblocks.mimecast.com
84 | 41.74.206.0/24 # subnet:za._netblocks.mimecast.com
85 | 194.104.110.21/32 # ip:de._netblocks.mimecast.com
86 | 63.128.21.0/24 # subnet:us._netblocks.mimecast.com
87 | 207.211.30.128/25 # subnet:us._netblocks.mimecast.com
88 | 216.145.217.0/24 # subnet:au._netblocks.mimecast.com
89 | 194.104.109.0/24 # subnet:de._netblocks.mimecast.com
90 | 207.82.80.0/24 # subnet:eu._netblocks.mimecast.com
91 | 207.211.30.64/26 # subnet:us._netblocks.mimecast.com
92 | 170.10.129.0/24 # subnet:us._netblocks.mimecast.com
93 | 41.74.196.0/22 # subnet:za._netblocks.mimecast.com
94 | 103.96.21.0/24 # subnet:au._netblocks.mimecast.com
95 | 124.47.150.0/24 # subnet:au._netblocks.mimecast.com
96 | 180.189.28.0/24 # subnet:au._netblocks.mimecast.com
97 | 193.7.207.0/25 # subnet:eu._netblocks.mimecast.com
98 | 147.28.36.0/24 # subnet:eu._netblocks.mimecast.com
99 | 51.163.158.0/24 # subnet:de._netblocks.mimecast.com
100 | 103.13.69.0/24 # subnet:au._netblocks.mimecast.com
101 | 207.211.41.113/32 # ip:us._netblocks.mimecast.com
102 | 194.104.110.240/28 # subnet:de._netblocks.mimecast.com
103 | 41.74.200.0/23 # subnet:za._netblocks.mimecast.com
104 | 205.139.110.0/24 # subnet:us._netblocks.mimecast.com
105 | 103.96.22.22/32 # ip:au._netblocks.mimecast.com
106 | 213.167.75.0/25 # subnet:eu._netblocks.mimecast.com
107 | 213.167.81.0/25 # subnet:eu._netblocks.mimecast.com
108 | 62.140.10.21/32 # ip:de._netblocks.mimecast.com
109 | 185.58.84.93/32 # ip:us._netblocks.mimecast.com
110 | 62.140.7.0/24 # subnet:de._netblocks.mimecast.com
111 | 41.74.192.0/22 # subnet:za._netblocks.mimecast.com
112 | 194.104.108.21/32 # ip:de._netblocks.mimecast.com
113 | 194.104.111.0/24 # subnet:de._netblocks.mimecast.com
114 | 51.163.159.21/32 # ip:de._netblocks.mimecast.com
115 | 91.220.42.0/24 # subnet:eu._netblocks.mimecast.com
116 | 195.130.217.0/24 # subnet:eu._netblocks.mimecast.com
117 | 193.7.206.0/25 # subnet:eu._netblocks.mimecast.com
118 | 103.96.22.96/28 # subnet:au._netblocks.mimecast.com
119 | 146.101.78.0/24 # subnet:eu._netblocks.mimecast.com
120 | 185.58.86.0/24 # subnet:eu._netblocks.mimecast.com
121 | 170.10.128.0/24 # subnet:us._netblocks.mimecast.com
122 | 194.104.108.240/29 # subnet:de._netblocks.mimecast.com
123 | 124.47.189.0/24 # subnet:au._netblocks.mimecast.com
124 | 207.211.31.0/25 # subnet:us._netblocks.mimecast.com
125 | 216.145.221.0/24 # subnet:us._netblocks.mimecast.com
126 | 185.58.85.0/24 # subnet:eu._netblocks.mimecast.com
127 | 216.205.24.0/24 # subnet:us._netblocks.mimecast.com
128 | 103.96.23.0/24 # subnet:au._netblocks.mimecast.com
129 | :99:v=spf1 ~all
130 | 0.0.0.0/1 # all other IPv4 addresses
131 | 128.0.0.0/1 # all other IP IPv4 addresses
132 | $DATASET ip6trie:_netblocks.mimecast.com _netblocks.mimecast.com
133 | :3:v=spf1 ip6:$ ~all
134 | :99:v=spf1 ~all
135 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
136 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:spf.protection.outlook.com @ 2023-10-09 10:25:23.099391
137 | # Source of truth: spf.protection.outlook.com - Will not work in production unless you replace a single record. e.g. include:spf.protection.outlook.com with include:%{ir}.spf.protection.outlook.com._spf.yourdomain.com
138 | # ^ spf.protection.outlook.com
139 | # ^ v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/14 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49 ip6:2a01:111:f403:8000::/50 ip6:2a01:111:f403:c000::/51 ip6:2a01:111:f403:f000::/52 -all
140 | # Depth:1
141 | # IP & Subnet: 9
142 | $DATASET ip4set:spf.protection.outlook.com spf.protection.outlook.com
143 | :3:v=spf1 ip4:$ -all
144 | 104.47.0.0/17 # subnet:spf.protection.outlook.com
145 | 52.100.0.0/14 # subnet:spf.protection.outlook.com
146 | 40.107.0.0/16 # subnet:spf.protection.outlook.com
147 | 40.92.0.0/15 # subnet:spf.protection.outlook.com
148 | :99:v=spf1 -all
149 | 0.0.0.0/1 # all other IPv4 addresses
150 | 128.0.0.0/1 # all other IP IPv4 addresses
151 | $DATASET ip6trie:spf.protection.outlook.com spf.protection.outlook.com
152 | :3:v=spf1 ip6:$ -all
153 | 2a01:111:f403:c000::/51 # spf.protection.outlook.com
154 | 2a01:111:f403:8000::/50 # spf.protection.outlook.com
155 | 2a01:111:f403:f000::/52 # spf.protection.outlook.com
156 | 2a01:111:f403::/49 # spf.protection.outlook.com
157 | 2a01:111:f400::/48 # spf.protection.outlook.com
158 | :99:v=spf1 -all
159 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
160 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:outbound.mailhop.org @ 2023-10-09 10:25:23.118386
161 | # Source of truth: outbound.mailhop.org - Will not work in production unless you replace a single record. e.g. include:outbound.mailhop.org with include:%{ir}.outbound.mailhop.org._spf.yourdomain.com
162 | # ^ outbound.mailhop.org
163 | # ^ v=spf1 ip4:52.26.49.97 ip4:52.10.99.51 ip4:52.28.59.28 ip4:52.28.6.212 ip4:3.127.8.113 ip4:54.186.27.61 ip4:52.28.168.19 ip4:52.58.19.153 ip4:52.58.96.151 ip4:52.58.97.209 ip4:52.88.73.233 ip4:54.187.71.48 ip4:54.149.36.10 ip4:54.68.34.165 ip4:54.186.22.84 ip4:54.191.214.3 ip4:3.125.66.160 ip4:54.213.22.21 ip4:52.29.21.168 ip4:100.20.105.3 ip4:44.233.67.66 ip4:54.185.97.28 include:_s0.outbound.mailhop.org -all
164 | # ^^ _s0.outbound.mailhop.org
165 | # ^^ v=spf1 ip4:54.190.72.79 ip4:18.156.67.42 ip4:54.69.130.42 ip4:3.124.88.253 ip4:52.29.156.81 ip4:52.28.246.64 ip4:54.68.138.64 ip4:3.121.156.226 ip4:54.191.214.36 ip4:44.233.38.249 ip4:44.233.99.129 ip4:54.149.240.58 ip4:18.156.94.234 ip4:54.186.10.118 ip4:3.125.148.246 ip4:54.187.63.214 ip4:54.149.35.133 ip4:52.10.130.167 ip4:54.148.38.162 ip4:54.187.206.49 ip4:44.233.143.45 include:_s1.outbound.mailhop.org -all
166 | # ^^^ _s1.outbound.mailhop.org
167 | # ^^^ v=spf1 ip4:35.157.29.171 ip4:54.191.158.99 ip4:54.186.172.23 ip4:54.186.57.195 ip4:54.148.219.64 ip4:54.148.153.48 ip4:54.148.30.215 ip4:54.148.222.11 ip4:54.186.218.12 ip4:52.28.251.132 ip4:52.28.147.211 ip4:52.58.109.202 ip4:54.187.218.212 ip4:54.187.213.119 ip4:54.149.210.130 ip4:54.148.165.188 ip4:54.149.155.156 ip4:54.148.113.140 ip4:35.156.234.212 ip4:54.149.205.143 include:_s2.outbound.mailhop.org -all
168 | # ^^^^ _s2.outbound.mailhop.org
169 | # ^^^^ v=spf1 ip4:54.149.206.185 ip4:54.244.192.240 ip4:54.191.151.194 ip4:54.200.247.200 ip4:54.200.129.228 ip4:35.160.111.225 ip4:23.83.208.0/20 ip4:191.252.57.0/25 ip4:34.219.98.66/32 ip4:52.34.31.144/32 ip4:46.232.183.0/24 ip4:104.232.45.0/24 ip4:199.10.31.235/32 ip4:54.245.125.39/32 ip4:172.255.62.10/32 ip4:172.255.62.11/32 ip4:177.153.0.128/25 ip4:177.153.0.130/32 ip4:199.10.31.236/32 ip4:54.214.232.113/32 include:_s3.outbound.mailhop.org -all
170 | # ^^^^^ _s3.outbound.mailhop.org
171 | # ^^^^^ v=spf1 ip4:103.18.109.138/32 -all
172 | # Depth:5
173 | # IP & Subnet: 84
174 | $DATASET ip4set:outbound.mailhop.org outbound.mailhop.org
175 | :3:v=spf1 ip4:$ -all
176 | 191.252.57.0/25 # subnet:_s2.outbound.mailhop.org
177 | 18.156.67.42 # ip:_s0.outbound.mailhop.org
178 | 177.153.0.130/32 # ip:_s2.outbound.mailhop.org
179 | 54.191.158.99 # ip:_s1.outbound.mailhop.org
180 | 54.191.214.3 # ip:outbound.mailhop.org
181 | 54.186.218.12 # ip:_s1.outbound.mailhop.org
182 | 52.58.109.202 # ip:_s1.outbound.mailhop.org
183 | 199.10.31.236/32 # ip:_s2.outbound.mailhop.org
184 | 54.200.129.228 # ip:_s2.outbound.mailhop.org
185 | 177.153.0.128/25 # subnet:_s2.outbound.mailhop.org
186 | 54.186.27.61 # ip:outbound.mailhop.org
187 | 54.148.222.11 # ip:_s1.outbound.mailhop.org
188 | 54.185.97.28 # ip:outbound.mailhop.org
189 | 54.149.240.58 # ip:_s0.outbound.mailhop.org
190 | 54.187.213.119 # ip:_s1.outbound.mailhop.org
191 | 54.187.218.212 # ip:_s1.outbound.mailhop.org
192 | 44.233.67.66 # ip:outbound.mailhop.org
193 | 23.83.208.0/20 # subnet:_s2.outbound.mailhop.org
194 | 52.10.99.51 # ip:outbound.mailhop.org
195 | 3.124.88.253 # ip:_s0.outbound.mailhop.org
196 | 52.34.31.144/32 # ip:_s2.outbound.mailhop.org
197 | 54.245.125.39/32 # ip:_s2.outbound.mailhop.org
198 | 54.148.165.188 # ip:_s1.outbound.mailhop.org
199 | 54.68.34.165 # ip:outbound.mailhop.org
200 | 54.187.206.49 # ip:_s0.outbound.mailhop.org
201 | 52.28.246.64 # ip:_s0.outbound.mailhop.org
202 | 54.148.219.64 # ip:_s1.outbound.mailhop.org
203 | 54.149.35.133 # ip:_s0.outbound.mailhop.org
204 | 52.10.130.167 # ip:_s0.outbound.mailhop.org
205 | 54.148.153.48 # ip:_s1.outbound.mailhop.org
206 | 54.148.38.162 # ip:_s0.outbound.mailhop.org
207 | 52.58.97.209 # ip:outbound.mailhop.org
208 | 52.88.73.233 # ip:outbound.mailhop.org
209 | 54.213.22.21 # ip:outbound.mailhop.org
210 | 44.233.38.249 # ip:_s0.outbound.mailhop.org
211 | 54.191.214.36 # ip:_s0.outbound.mailhop.org
212 | 199.10.31.235/32 # ip:_s2.outbound.mailhop.org
213 | 103.18.109.138/32 # ip:_s3.outbound.mailhop.org
214 | 54.200.247.200 # ip:_s2.outbound.mailhop.org
215 | 52.58.96.151 # ip:outbound.mailhop.org
216 | 54.190.72.79 # ip:_s0.outbound.mailhop.org
217 | 35.156.234.212 # ip:_s1.outbound.mailhop.org
218 | 100.20.105.3 # ip:outbound.mailhop.org
219 | 54.186.22.84 # ip:outbound.mailhop.org
220 | 46.232.183.0/24 # subnet:_s2.outbound.mailhop.org
221 | 54.69.130.42 # ip:_s0.outbound.mailhop.org
222 | 3.121.156.226 # ip:_s0.outbound.mailhop.org
223 | 54.186.10.118 # ip:_s0.outbound.mailhop.org
224 | 18.156.94.234 # ip:_s0.outbound.mailhop.org
225 | 54.148.30.215 # ip:_s1.outbound.mailhop.org
226 | 44.233.143.45 # ip:_s0.outbound.mailhop.org
227 | 54.148.113.140 # ip:_s1.outbound.mailhop.org
228 | 52.29.156.81 # ip:_s0.outbound.mailhop.org
229 | 54.68.138.64 # ip:_s0.outbound.mailhop.org
230 | 52.28.251.132 # ip:_s1.outbound.mailhop.org
231 | 35.157.29.171 # ip:_s1.outbound.mailhop.org
232 | 52.28.59.28 # ip:outbound.mailhop.org
233 | 52.28.147.211 # ip:_s1.outbound.mailhop.org
234 | 172.255.62.11/32 # ip:_s2.outbound.mailhop.org
235 | 3.127.8.113 # ip:outbound.mailhop.org
236 | 54.186.57.195 # ip:_s1.outbound.mailhop.org
237 | 52.26.49.97 # ip:outbound.mailhop.org
238 | 54.149.206.185 # ip:_s2.outbound.mailhop.org
239 | 172.255.62.10/32 # ip:_s2.outbound.mailhop.org
240 | 44.233.99.129 # ip:_s0.outbound.mailhop.org
241 | 52.29.21.168 # ip:outbound.mailhop.org
242 | 104.232.45.0/24 # subnet:_s2.outbound.mailhop.org
243 | 52.28.168.19 # ip:outbound.mailhop.org
244 | 54.187.71.48 # ip:outbound.mailhop.org
245 | 54.244.192.240 # ip:_s2.outbound.mailhop.org
246 | 54.191.151.194 # ip:_s2.outbound.mailhop.org
247 | 54.149.36.10 # ip:outbound.mailhop.org
248 | 54.149.205.143 # ip:_s1.outbound.mailhop.org
249 | 54.214.232.113/32 # ip:_s2.outbound.mailhop.org
250 | 54.149.155.156 # ip:_s1.outbound.mailhop.org
251 | 34.219.98.66/32 # ip:_s2.outbound.mailhop.org
252 | 54.149.210.130 # ip:_s1.outbound.mailhop.org
253 | 3.125.148.246 # ip:_s0.outbound.mailhop.org
254 | 3.125.66.160 # ip:outbound.mailhop.org
255 | 54.187.63.214 # ip:_s0.outbound.mailhop.org
256 | 35.160.111.225 # ip:_s2.outbound.mailhop.org
257 | 52.28.6.212 # ip:outbound.mailhop.org
258 | 52.58.19.153 # ip:outbound.mailhop.org
259 | 54.186.172.23 # ip:_s1.outbound.mailhop.org
260 | :99:v=spf1 -all
261 | 0.0.0.0/1 # all other IPv4 addresses
262 | 128.0.0.0/1 # all other IP IPv4 addresses
263 | $DATASET ip6trie:outbound.mailhop.org outbound.mailhop.org
264 | :3:v=spf1 ip6:$ -all
265 | :99:v=spf1 -all
266 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
267 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:spf.messagelabs.com @ 2023-10-09 10:25:23.209503
268 | # Source of truth: spf.messagelabs.com - Will not work in production unless you replace a single record. e.g. include:spf.messagelabs.com with include:%{ir}.spf.messagelabs.com._spf.yourdomain.com
269 | # ^ spf.messagelabs.com
270 | # ^ v=spf1 include:nets1.spf.messagelabs.com include:nets2.spf.messagelabs.com ~all
271 | # ^^ nets1.spf.messagelabs.com
272 | # ^^ v=spf1 ip4:85.158.136.0/21 ip4:193.109.254.0/23 ip4:194.106.220.0/23 ip4:195.245.230.0/23 ip4:95.131.104.0/21 ip4:46.226.48.0/21
273 | # ^^^ nets2.spf.messagelabs.com
274 | # ^^^ v=spf1 ip4:216.82.240.0/20 ip4:67.219.240.0/20 ip4:117.120.16.0/21 ip4:103.9.96.0/22
275 | # Depth:3
276 | # IP & Subnet: 10
277 | $DATASET ip4set:spf.messagelabs.com spf.messagelabs.com
278 | :3:v=spf1 ip4:$ ~all
279 | 117.120.16.0/21 # subnet:nets2.spf.messagelabs.com
280 | 85.158.136.0/21 # subnet:nets1.spf.messagelabs.com
281 | 67.219.240.0/20 # subnet:nets2.spf.messagelabs.com
282 | 95.131.104.0/21 # subnet:nets1.spf.messagelabs.com
283 | 194.106.220.0/23 # subnet:nets1.spf.messagelabs.com
284 | 193.109.254.0/23 # subnet:nets1.spf.messagelabs.com
285 | 216.82.240.0/20 # subnet:nets2.spf.messagelabs.com
286 | 46.226.48.0/21 # subnet:nets1.spf.messagelabs.com
287 | 195.245.230.0/23 # subnet:nets1.spf.messagelabs.com
288 | 103.9.96.0/22 # subnet:nets2.spf.messagelabs.com
289 | :99:v=spf1 ~all
290 | 0.0.0.0/1 # all other IPv4 addresses
291 | 128.0.0.0/1 # all other IP IPv4 addresses
292 | $DATASET ip6trie:spf.messagelabs.com spf.messagelabs.com
293 | :3:v=spf1 ip6:$ ~all
294 | :99:v=spf1 ~all
295 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
296 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:mailgun.org @ 2023-10-09 10:25:23.253500
297 | # Source of truth: mailgun.org - Will not work in production unless you replace a single record. e.g. include:mailgun.org with include:%{ir}.mailgun.org._spf.yourdomain.com
298 | # ^ mailgun.org
299 | # ^ v=spf1 include:_spf.mailgun.org include:_spf.eu.mailgun.org -all
300 | # ^^ _spf.mailgun.org
301 | # ^^ v=spf1 ip4:209.61.151.0/24 ip4:166.78.68.0/22 ip4:198.61.254.0/23 ip4:192.237.158.0/23 ip4:23.253.182.0/23 ip4:104.130.96.0/28 ip4:146.20.113.0/24 ip4:146.20.191.0/24 ip4:159.135.224.0/20 ip4:69.72.32.0/20 ip4:104.130.122.0/23 ip4:146.20.112.0/26 ip4:161.38.192.0/20 ip4:143.55.224.0/21 ip4:143.55.232.0/22 ip4:159.112.240.0/20 ip4:198.244.48.0/20 ip4:204.220.160.0/20 ~all
302 | # ^^^ _spf.eu.mailgun.org
303 | # ^^^ v=spf1 ip4:141.193.32.0/23 ip4:159.135.140.80/29 ip4:159.135.132.128/25 ip4:161.38.204.0/22 ip4:87.253.232.0/21 ip4:185.189.236.0/22 ip4:185.211.120.0/22 ip4:185.250.236.0/22 ip4:143.55.236.0/22 ip4:198.244.60.0/22 ~all
304 | # Depth:3
305 | # IP & Subnet: 28
306 | $DATASET ip4set:mailgun.org mailgun.org
307 | :3:v=spf1 ip4:$ ~all
308 | 159.135.132.128/25 # subnet:_spf.eu.mailgun.org
309 | 143.55.236.0/22 # subnet:_spf.eu.mailgun.org
310 | 143.55.232.0/22 # subnet:_spf.mailgun.org
311 | 185.250.236.0/22 # subnet:_spf.eu.mailgun.org
312 | 192.237.158.0/23 # subnet:_spf.mailgun.org
313 | 209.61.151.0/24 # subnet:_spf.mailgun.org
314 | 143.55.224.0/21 # subnet:_spf.mailgun.org
315 | 198.244.48.0/20 # subnet:_spf.mailgun.org
316 | 146.20.191.0/24 # subnet:_spf.mailgun.org
317 | 87.253.232.0/21 # subnet:_spf.eu.mailgun.org
318 | 204.220.160.0/20 # subnet:_spf.mailgun.org
319 | 146.20.112.0/26 # subnet:_spf.mailgun.org
320 | 185.211.120.0/22 # subnet:_spf.eu.mailgun.org
321 | 159.135.224.0/20 # subnet:_spf.mailgun.org
322 | 146.20.113.0/24 # subnet:_spf.mailgun.org
323 | 159.112.240.0/20 # subnet:_spf.mailgun.org
324 | 141.193.32.0/23 # subnet:_spf.eu.mailgun.org
325 | 104.130.96.0/28 # subnet:_spf.mailgun.org
326 | 159.135.140.80/29 # subnet:_spf.eu.mailgun.org
327 | 198.244.60.0/22 # subnet:_spf.eu.mailgun.org
328 | 161.38.204.0/22 # subnet:_spf.eu.mailgun.org
329 | 69.72.32.0/20 # subnet:_spf.mailgun.org
330 | 161.38.192.0/20 # subnet:_spf.mailgun.org
331 | 23.253.182.0/23 # subnet:_spf.mailgun.org
332 | 104.130.122.0/23 # subnet:_spf.mailgun.org
333 | 166.78.68.0/22 # subnet:_spf.mailgun.org
334 | 185.189.236.0/22 # subnet:_spf.eu.mailgun.org
335 | 198.61.254.0/23 # subnet:_spf.mailgun.org
336 | :99:v=spf1 ~all
337 | 0.0.0.0/1 # all other IPv4 addresses
338 | 128.0.0.0/1 # all other IP IPv4 addresses
339 | $DATASET ip6trie:mailgun.org mailgun.org
340 | :3:v=spf1 ip6:$ ~all
341 | :99:v=spf1 ~all
342 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
343 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:sendgrid.net @ 2023-10-09 10:25:23.386499
344 | # Source of truth: sendgrid.net - Will not work in production unless you replace a single record. e.g. include:sendgrid.net with include:%{ir}.sendgrid.net._spf.yourdomain.com
345 | # ^ sendgrid.net
346 | # ^ v=spf1 ip4:167.89.0.0/17 ip4:208.117.48.0/20 ip4:50.31.32.0/19 ip4:198.37.144.0/20 ip4:198.21.0.0/21 ip4:192.254.112.0/20 ip4:168.245.0.0/17 ip4:149.72.0.0/16 ip4:159.183.0.0/16 include:ab.sendgrid.net ~all
347 | # ^^ ab.sendgrid.net
348 | # ^^ v=spf1 ip4:223.165.113.0/24 ip4:223.165.115.0/24 ip4:223.165.118.0/23 ip4:223.165.120.0/23 ~all
349 | # Depth:2
350 | # IP & Subnet: 13
351 | $DATASET ip4set:sendgrid.net sendgrid.net
352 | :3:v=spf1 ip4:$ ~all
353 | 198.37.144.0/20 # subnet:sendgrid.net
354 | 168.245.0.0/17 # subnet:sendgrid.net
355 | 149.72.0.0/16 # subnet:sendgrid.net
356 | 192.254.112.0/20 # subnet:sendgrid.net
357 | 159.183.0.0/16 # subnet:sendgrid.net
358 | 223.165.115.0/24 # subnet:ab.sendgrid.net
359 | 223.165.120.0/23 # subnet:ab.sendgrid.net
360 | 208.117.48.0/20 # subnet:sendgrid.net
361 | 223.165.118.0/23 # subnet:ab.sendgrid.net
362 | 50.31.32.0/19 # subnet:sendgrid.net
363 | 167.89.0.0/17 # subnet:sendgrid.net
364 | 223.165.113.0/24 # subnet:ab.sendgrid.net
365 | 198.21.0.0/21 # subnet:sendgrid.net
366 | :99:v=spf1 ~all
367 | 0.0.0.0/1 # all other IPv4 addresses
368 | 128.0.0.0/1 # all other IP IPv4 addresses
369 | $DATASET ip6trie:sendgrid.net sendgrid.net
370 | :3:v=spf1 ip6:$ ~all
371 | :99:v=spf1 ~all
372 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
373 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:service-now.com @ 2023-10-09 10:25:23.412512
374 | # Source of truth: service-now.com - Will not work in production unless you replace a single record. e.g. include:service-now.com with include:%{ir}.service-now.com._spf.yourdomain.com
375 | # ^ service-now.com
376 | # ^ v=spf1 a:b.spf.service-now.com a:c.spf.service-now.com a:d.spf.service-now.com ~all
377 | # ^^ a:b.spf.service-now.com
378 | # ^^^ a:c.spf.service-now.com
379 | # ^^^^ a:d.spf.service-now.com
380 | # Depth:4
381 | # IP & Subnet: 65
382 | $DATASET ip4set:service-now.com service-now.com
383 | :3:v=spf1 ip4:$ ~all
384 | 37.98.232.26 # a:c.spf.service-now.com
385 | 199.91.136.28 # a:c.spf.service-now.com
386 | 149.96.1.26 # a:d.spf.service-now.com
387 | 149.96.2.26 # a:d.spf.service-now.com
388 | 148.139.124.23 # a:b.spf.service-now.com
389 | 199.91.140.28 # a:d.spf.service-now.com
390 | 149.96.221.2 # a:d.spf.service-now.com
391 | 148.139.104.17 # a:d.spf.service-now.com
392 | 149.96.5.7 # a:b.spf.service-now.com
393 | 148.139.124.22 # a:b.spf.service-now.com
394 | 149.96.6.2 # a:b.spf.service-now.com
395 | 199.91.139.22 # a:c.spf.service-now.com
396 | 148.139.105.17 # a:d.spf.service-now.com
397 | 149.96.133.2 # a:d.spf.service-now.com
398 | 199.91.140.26 # a:c.spf.service-now.com
399 | 148.139.125.22 # a:b.spf.service-now.com
400 | 148.139.125.21 # a:b.spf.service-now.com
401 | 148.139.3.2 # a:c.spf.service-now.com
402 | 149.96.6.3 # a:b.spf.service-now.com
403 | 199.91.140.28 # a:b.spf.service-now.com
404 | 148.139.0.31 # a:c.spf.service-now.com
405 | 148.139.125.24 # a:b.spf.service-now.com
406 | 148.139.1.2 # a:c.spf.service-now.com
407 | 149.96.6.6 # a:b.spf.service-now.com
408 | 149.96.6.7 # a:b.spf.service-now.com
409 | 199.91.141.23 # a:c.spf.service-now.com
410 | 199.91.140.28 # a:c.spf.service-now.com
411 | 149.96.6.209 # a:b.spf.service-now.com
412 | 199.91.141.145 # a:c.spf.service-now.com
413 | 37.98.232.2 # a:c.spf.service-now.com
414 | 37.98.232.12 # a:c.spf.service-now.com
415 | 199.91.141.24 # a:c.spf.service-now.com
416 | 37.98.235.2 # a:c.spf.service-now.com
417 | 103.23.64.2 # a:d.spf.service-now.com
418 | 37.98.234.2 # a:c.spf.service-now.com
419 | 199.91.139.145 # a:c.spf.service-now.com
420 | 199.91.137.2 # a:c.spf.service-now.com
421 | 149.96.5.6 # a:b.spf.service-now.com
422 | 199.91.139.23 # a:c.spf.service-now.com
423 | 149.96.194.2 # a:d.spf.service-now.com
424 | 149.96.5.209 # a:b.spf.service-now.com
425 | 103.23.67.26 # a:d.spf.service-now.com
426 | 199.91.136.28 # a:b.spf.service-now.com
427 | 149.96.5.3 # a:b.spf.service-now.com
428 | 149.96.13.2 # a:c.spf.service-now.com
429 | 199.91.141.22 # a:c.spf.service-now.com
430 | 148.139.124.24 # a:b.spf.service-now.com
431 | 148.139.104.16 # a:d.spf.service-now.com
432 | 148.139.105.16 # a:d.spf.service-now.com
433 | 149.96.195.2 # a:d.spf.service-now.com
434 | 199.91.136.26 # a:c.spf.service-now.com
435 | 199.91.136.28 # a:d.spf.service-now.com
436 | 148.139.124.21 # a:b.spf.service-now.com
437 | 148.139.1.31 # a:c.spf.service-now.com
438 | 199.91.139.24 # a:c.spf.service-now.com
439 | 148.139.0.2 # a:c.spf.service-now.com
440 | 103.23.65.2 # a:d.spf.service-now.com
441 | 149.96.5.2 # a:b.spf.service-now.com
442 | 149.96.14.2 # a:c.spf.service-now.com
443 | 149.96.132.2 # a:d.spf.service-now.com
444 | 103.23.66.26 # a:d.spf.service-now.com
445 | 199.91.137.26 # a:c.spf.service-now.com
446 | 148.139.2.2 # a:c.spf.service-now.com
447 | 148.139.125.23 # a:b.spf.service-now.com
448 | 149.96.220.2 # a:d.spf.service-now.com
449 | :99:v=spf1 ~all
450 | 0.0.0.0/1 # all other IPv4 addresses
451 | 128.0.0.0/1 # all other IP IPv4 addresses
452 | $DATASET ip6trie:service-now.com service-now.com
453 | :3:v=spf1 ip6:$ ~all
454 | :99:v=spf1 ~all
455 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
456 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/sendgrid-net:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:sendgrid.net @ 2023-05-24 20:54:47.991805
13 | # Source of truth: sendgrid.net - Will not work in production unless you replace a single record. e.g. include:sendgrid.net with include:%{ir}.sendgrid.net._spf.yourdomain.com
14 | # ^ sendgrid.net
15 | # ^ v=spf1 ip4:167.89.0.0/17 ip4:208.117.48.0/20 ip4:50.31.32.0/19 ip4:198.37.144.0/20 ip4:198.21.0.0/21 ip4:192.254.112.0/20 ip4:168.245.0.0/17 ip4:149.72.0.0/16 ip4:159.183.0.0/16 include:ab.sendgrid.net ~all
16 | # ^^ ab.sendgrid.net
17 | # ^^ v=spf1 ip4:223.165.113.0/24 ip4:223.165.115.0/24 ip4:223.165.118.0/23 ip4:223.165.120.0/23 ~all
18 | # Depth:2
19 | # IP & Subnet: 13
20 | $DATASET ip4set:sendgrid.net sendgrid.net
21 | :3:v=spf1 ip4:$ ~all
22 | 167.89.0.0/17 # subnet:sendgrid.net
23 | 208.117.48.0/20 # subnet:sendgrid.net
24 | 50.31.32.0/19 # subnet:sendgrid.net
25 | 198.37.144.0/20 # subnet:sendgrid.net
26 | 198.21.0.0/21 # subnet:sendgrid.net
27 | 192.254.112.0/20 # subnet:sendgrid.net
28 | 168.245.0.0/17 # subnet:sendgrid.net
29 | 149.72.0.0/16 # subnet:sendgrid.net
30 | 159.183.0.0/16 # subnet:sendgrid.net
31 | 223.165.113.0/24 # subnet:ab.sendgrid.net
32 | 223.165.115.0/24 # subnet:ab.sendgrid.net
33 | 223.165.118.0/23 # subnet:ab.sendgrid.net
34 | 223.165.120.0/23 # subnet:ab.sendgrid.net
35 | :99:v=spf1 ~all
36 | 0.0.0.0/1 # all other IPv4 addresses
37 | 128.0.0.0/1 # all other IP IPv4 addresses
38 | $DATASET ip6trie:sendgrid.net sendgrid.net
39 | :3:v=spf1 ip6:$ ~all
40 | :99:v=spf1 ~all
41 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
42 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/service-now-com:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:service-now.com @ 2023-05-24 20:54:48.042797
13 | # Source of truth: service-now.com - Will not work in production unless you replace a single record. e.g. include:service-now.com with include:%{ir}.service-now.com._spf.yourdomain.com
14 | # ^ service-now.com
15 | # ^ v=spf1 a:b.spf.service-now.com a:c.spf.service-now.com a:d.spf.service-now.com ~all
16 | # DNS Resolution Error - AAAA:b.spf.service-now.com
17 | # ^^ a:b.spf.service-now.com
18 | # DNS Resolution Error - AAAA:c.spf.service-now.com
19 | # ^^^ a:c.spf.service-now.com
20 | # DNS Resolution Error - AAAA:d.spf.service-now.com
21 | # ^^^^ a:d.spf.service-now.com
22 | # Depth:4
23 | # IP & Subnet: 57
24 | $DATASET ip4set:service-now.com service-now.com
25 | :3:v=spf1 ip4:$ ~all
26 | 149.96.5.2 # a:b.spf.service-now.com
27 | 149.96.5.209 # a:b.spf.service-now.com
28 | 149.96.5.3 # a:b.spf.service-now.com
29 | 149.96.5.6 # a:b.spf.service-now.com
30 | 149.96.5.7 # a:b.spf.service-now.com
31 | 149.96.6.2 # a:b.spf.service-now.com
32 | 149.96.6.209 # a:b.spf.service-now.com
33 | 149.96.6.3 # a:b.spf.service-now.com
34 | 149.96.6.6 # a:b.spf.service-now.com
35 | 149.96.6.7 # a:b.spf.service-now.com
36 | 199.91.136.28 # a:b.spf.service-now.com
37 | 199.91.140.28 # a:b.spf.service-now.com
38 | 148.139.0.2 # a:c.spf.service-now.com
39 | 148.139.0.31 # a:c.spf.service-now.com
40 | 148.139.1.2 # a:c.spf.service-now.com
41 | 148.139.1.31 # a:c.spf.service-now.com
42 | 148.139.2.2 # a:c.spf.service-now.com
43 | 148.139.3.2 # a:c.spf.service-now.com
44 | 149.96.13.2 # a:c.spf.service-now.com
45 | 149.96.14.2 # a:c.spf.service-now.com
46 | 199.91.136.26 # a:c.spf.service-now.com
47 | 199.91.136.28 # a:c.spf.service-now.com
48 | 199.91.137.2 # a:c.spf.service-now.com
49 | 199.91.137.26 # a:c.spf.service-now.com
50 | 199.91.139.145 # a:c.spf.service-now.com
51 | 199.91.139.22 # a:c.spf.service-now.com
52 | 199.91.139.23 # a:c.spf.service-now.com
53 | 199.91.139.24 # a:c.spf.service-now.com
54 | 199.91.140.26 # a:c.spf.service-now.com
55 | 199.91.140.28 # a:c.spf.service-now.com
56 | 199.91.141.145 # a:c.spf.service-now.com
57 | 199.91.141.22 # a:c.spf.service-now.com
58 | 199.91.141.23 # a:c.spf.service-now.com
59 | 199.91.141.24 # a:c.spf.service-now.com
60 | 37.98.232.12 # a:c.spf.service-now.com
61 | 37.98.232.2 # a:c.spf.service-now.com
62 | 37.98.232.26 # a:c.spf.service-now.com
63 | 37.98.234.2 # a:c.spf.service-now.com
64 | 37.98.235.2 # a:c.spf.service-now.com
65 | 103.23.64.2 # a:d.spf.service-now.com
66 | 103.23.65.2 # a:d.spf.service-now.com
67 | 103.23.66.26 # a:d.spf.service-now.com
68 | 103.23.67.26 # a:d.spf.service-now.com
69 | 148.139.104.16 # a:d.spf.service-now.com
70 | 148.139.104.17 # a:d.spf.service-now.com
71 | 148.139.105.16 # a:d.spf.service-now.com
72 | 148.139.105.17 # a:d.spf.service-now.com
73 | 149.96.1.26 # a:d.spf.service-now.com
74 | 149.96.132.2 # a:d.spf.service-now.com
75 | 149.96.133.2 # a:d.spf.service-now.com
76 | 149.96.194.2 # a:d.spf.service-now.com
77 | 149.96.195.2 # a:d.spf.service-now.com
78 | 149.96.2.26 # a:d.spf.service-now.com
79 | 149.96.220.2 # a:d.spf.service-now.com
80 | 149.96.221.2 # a:d.spf.service-now.com
81 | 199.91.136.28 # a:d.spf.service-now.com
82 | 199.91.140.28 # a:d.spf.service-now.com
83 | :99:v=spf1 ~all
84 | 0.0.0.0/1 # all other IPv4 addresses
85 | 128.0.0.0/1 # all other IP IPv4 addresses
86 | $DATASET ip6trie:service-now.com service-now.com
87 | :3:v=spf1 ip6:$ ~all
88 | :99:v=spf1 ~all
89 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
90 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/spf-messagelabs-com:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:spf.messagelabs.com @ 2023-05-24 20:54:47.610188
13 | # Source of truth: spf.messagelabs.com - Will not work in production unless you replace a single record. e.g. include:spf.messagelabs.com with include:%{ir}.spf.messagelabs.com._spf.yourdomain.com
14 | # ^ spf.messagelabs.com
15 | # ^ v=spf1 include:nets1.spf.messagelabs.com include:nets2.spf.messagelabs.com ~all
16 | # ^^ nets1.spf.messagelabs.com
17 | # ^^ v=spf1 ip4:85.158.136.0/21 ip4:193.109.254.0/23 ip4:194.106.220.0/23 ip4:195.245.230.0/23 ip4:95.131.104.0/21 ip4:46.226.48.0/21
18 | # ^^^ nets2.spf.messagelabs.com
19 | # ^^^ v=spf1 ip4:216.82.240.0/20 ip4:67.219.240.0/20 ip4:117.120.16.0/21 ip4:103.9.96.0/22
20 | # Depth:3
21 | # IP & Subnet: 10
22 | $DATASET ip4set:spf.messagelabs.com spf.messagelabs.com
23 | :3:v=spf1 ip4:$ ~all
24 | 85.158.136.0/21 # subnet:nets1.spf.messagelabs.com
25 | 193.109.254.0/23 # subnet:nets1.spf.messagelabs.com
26 | 194.106.220.0/23 # subnet:nets1.spf.messagelabs.com
27 | 195.245.230.0/23 # subnet:nets1.spf.messagelabs.com
28 | 95.131.104.0/21 # subnet:nets1.spf.messagelabs.com
29 | 46.226.48.0/21 # subnet:nets1.spf.messagelabs.com
30 | 216.82.240.0/20 # subnet:nets2.spf.messagelabs.com
31 | 67.219.240.0/20 # subnet:nets2.spf.messagelabs.com
32 | 117.120.16.0/21 # subnet:nets2.spf.messagelabs.com
33 | 103.9.96.0/22 # subnet:nets2.spf.messagelabs.com
34 | :99:v=spf1 ~all
35 | 0.0.0.0/1 # all other IPv4 addresses
36 | 128.0.0.0/1 # all other IP IPv4 addresses
37 | $DATASET ip6trie:spf.messagelabs.com spf.messagelabs.com
38 | :3:v=spf1 ip6:$ ~all
39 | :99:v=spf1 ~all
40 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
41 |
--------------------------------------------------------------------------------
/expurgate-resolver/output/spf-protection-outlook-com:
--------------------------------------------------------------------------------
1 | #
2 | # ______ _
3 | # | ____| | |
4 | # | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___
5 | # | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \
6 | # | |____ > <| |_) | |_| | | | (_| | (_| | || __/
7 | # |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|
8 | # | | __/ |
9 | # |_| |___/
10 | #
11 | # https://xpg8.tk | https://github.com/smck83/expurgate
12 | # Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:spf.protection.outlook.com @ 2023-05-24 20:54:47.405207
13 | # Source of truth: spf.protection.outlook.com - Will not work in production unless you replace a single record. e.g. include:spf.protection.outlook.com with include:%{ir}.spf.protection.outlook.com._spf.yourdomain.com
14 | # ^ spf.protection.outlook.com
15 | # ^ v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/14 ip4:104.47.0.0/17 ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49 ip6:2a01:111:f403:8000::/50 ip6:2a01:111:f403:c000::/51 ip6:2a01:111:f403:f000::/52 -all
16 | # Depth:1
17 | # IP & Subnet: 9
18 | $DATASET ip4set:spf.protection.outlook.com spf.protection.outlook.com
19 | :3:v=spf1 ip4:$ -all
20 | 40.92.0.0/15 # subnet:spf.protection.outlook.com
21 | 40.107.0.0/16 # subnet:spf.protection.outlook.com
22 | 52.100.0.0/14 # subnet:spf.protection.outlook.com
23 | 104.47.0.0/17 # subnet:spf.protection.outlook.com
24 | :99:v=spf1 -all
25 | 0.0.0.0/1 # all other IPv4 addresses
26 | 128.0.0.0/1 # all other IP IPv4 addresses
27 | $DATASET ip6trie:spf.protection.outlook.com spf.protection.outlook.com
28 | :3:v=spf1 ip6:$ -all
29 | 2a01:111:f400::/48 # spf.protection.outlook.com
30 | 2a01:111:f403::/49 # spf.protection.outlook.com
31 | 2a01:111:f403:8000::/50 # spf.protection.outlook.com
32 | 2a01:111:f403:c000::/51 # spf.protection.outlook.com
33 | 2a01:111:f403:f000::/52 # spf.protection.outlook.com
34 | :99:v=spf1 -all
35 | 0:0:0:0:0:0:0:0/0 # all other IPv6 addresses
36 |
--------------------------------------------------------------------------------
/expurgate-resolver/resolver.py:
--------------------------------------------------------------------------------
1 | # author: https://github.com/smck83/
2 | xpg8logo = ["# "]
3 | xpg8logo.append(r"# ______ _ ")
4 | xpg8logo.append(r"# | ____| | | ")
5 | xpg8logo.append(r"# | |__ __ ___ __ _ _ _ __ __ _ __ _| |_ ___ ")
6 | xpg8logo.append("# | __| \ \/ / '_ \| | | | '__/ _` |/ _` | __/ _ \\")
7 | xpg8logo.append(r"# | |____ > <| |_) | |_| | | | (_| | (_| | || __/")
8 | xpg8logo.append(r"# |______/_/\_\ .__/ \__,_|_| \__, |\__,_|\__\___|")
9 | xpg8logo.append(r"# | | __/ | ")
10 | xpg8logo.append(r"# |_| |___/ ")
11 | xpg8logo.append(r"#")
12 | xpg8logo.append("# https://xpg8.ehlo.email | https://github.com/smck83/expurgate ")
13 | from sys import stdout
14 | from time import sleep
15 | from time import strftime
16 | import dns.resolver
17 | import re
18 | from datetime import datetime
19 | import os
20 | import shutil
21 | import time
22 | import math
23 | import requests
24 | import json
25 | import ipaddress
26 | from jsonpath_ng.ext import parse
27 |
28 | paddingchar = "^"
29 | spfActionValue ="~all" # default spfAction if lookup fails or not present
30 | ipmonitorCompare = {}
31 | loopcount = 0
32 | lastChangeTime = "No changes"
33 | totalChangeCount = 0
34 |
35 | if 'RESTDB_URL' in os.environ:
36 | restdb_url = os.environ['RESTDB_URL']
37 | else:
38 | restdb_url = None
39 |
40 | if 'RESTDB_KEY' in os.environ:
41 | restdb_key = os.environ['RESTDB_KEY']
42 | else:
43 | restdb_key = None
44 |
45 | if 'UPTIMEKUMA_PUSH_URL' in os.environ and re.match('^http.*\/api\/push\/.*\&ping\=',os.environ['UPTIMEKUMA_PUSH_URL'], re.IGNORECASE):
46 | uptimekumapushurl = os.environ['UPTIMEKUMA_PUSH_URL']
47 | else:
48 | uptimekumapushurl = None
49 |
50 | if 'SOURCE_PREFIX_OFF' in os.environ:
51 | source_prefix_off = os.environ['SOURCE_PREFIX_OFF']
52 | else:
53 | source_prefix_off = False # set to True to be able to run against root domain, for vendor flattening e.g. replace include:_spf.google.com which needs 3 lookups with include:%{ir}._spf.google.com._spf.yourdomain.com or include:outbound.mailhop.org which needs 4 lookups with include:%{ir}.outbound.mailhop.org._spf.yourdomain.com
54 |
55 | if 'SOURCE_PREFIX' in os.environ:
56 | source_prefix = os.environ['SOURCE_PREFIX']
57 | else:
58 | source_prefix = "_xpg8"
59 |
60 | if 'RUNNING_CONFIG_ON' in os.environ:
61 | runningconfigon = int(os.environ['RUNNING_CONFIG_ON'])
62 | else:
63 | runningconfigon = 1 #if not specified, running_config is on.
64 | # runningconfigon = 1
65 | def restdb(restdb_url,restdb_key):
66 | payload={}
67 | headers = {
68 | 'Content-Type': 'application/json',
69 | 'x-apikey': restdb_key
70 | }
71 |
72 | domains = []
73 | response = requests.request("GET", restdb_url, headers=headers, data=payload)
74 | out = response.text
75 | aList = json.loads(out)
76 | jsonpath_expression = parse("$..domain")
77 |
78 | for match in jsonpath_expression.find(aList):
79 | domains.append(match.value)
80 |
81 | return domains
82 |
83 | if 'MY_DOMAINS' in os.environ and restdb_url == None:
84 | domains = os.environ['MY_DOMAINS']
85 | mydomains = domains.split(' ') # convert input string to list
86 | mydomains = [domain for domain in mydomains if '.' in domain] # confirm domain contains a fullstop
87 | mydomains = list(dict.fromkeys(mydomains)) # dedupe the list of domains
88 | elif restdb_url != None:
89 | mydomains = restdb(restdb_url,restdb_key)
90 | else:
91 | source_prefix_off = True
92 |
93 | mydomains = ['_spf.google.com','_netblocks.mimecast.com','spf.protection.outlook.com','outbound.mailhop.org','spf.messagelabs.com','mailgun.org','sendgrid.net','service-now.com'] # demo mode
94 | print("MY_DOMAIN not set, running in demo mode using " + str(mydomains))
95 |
96 | totaldomaincount = len(mydomains)
97 |
98 | if 'DELAY' in os.environ and int(os.environ['DELAY']) > 29:
99 | delayBetweenRun = os.environ['DELAY']
100 | else:
101 | delayBetweenRun = 300 #default to 5 minutes
102 | print("Running delay of : " + str(delayBetweenRun))
103 |
104 | def write2disk(src_path,dst_path,myrbldnsdconfig):
105 | with open(src_path, 'w') as fp:
106 | for item in myrbldnsdconfig:
107 | # write each item on a new line
108 | fp.write("%s\n" % item)
109 | shutil.move(src_path, dst_path)
110 | print("[" + str(loopcount) + ': CHANGE DETECTED] Writing config file:' + dst_path)
111 |
112 | def ipInSubnet(an_address,a_network):
113 | an_address = ipaddress.ip_address(an_address)
114 | a_network = ipaddress.ip_network(a_network)
115 | address_in_network = an_address in a_network
116 | return address_in_network
117 |
118 | def uptimeKumaPush (url):
119 | try:
120 | x = requests.get(url)
121 | except:
122 | print("ERROR: Uptime Kuma - push notification")
123 |
124 | def dnsLookup(domain,type,countDepth="on"):
125 | global depth
126 | global cacheHit
127 | lookupKey = domain + "-" + type
128 | mydomains_source_success_status = False
129 | if lookupKey not in dnsCache:
130 | try:
131 | lookup = [dns_record.to_text() for dns_record in dns.resolver.resolve(domain, type).rrset]
132 | except Exception as e:
133 | error = "DNS Resolution Error - " + type + ":" + domain
134 | if type != "AAAA":
135 | header.append("# " + error)
136 | print(error)
137 | print(e)
138 | if depth == 0 and type=="TXT":
139 | mydomains_source_failure.append(domain)
140 | else:
141 | if depth == 0 and type=="TXT":
142 | for record in lookup:
143 | if record != None and re.match('^"v=spf1 ', record, re.IGNORECASE): # check if the first lookup record has a TXT SPF record.
144 | mydomains_source_success_status = True
145 |
146 | if mydomains_source_success_status == True: # using boolean, so as to only add 1 record (incase a domain has multiple v=spf1 records)
147 | mydomains_source_success.append(domain)
148 | else:
149 | mydomains_source_failure.append(domain) # has TXT record, but no SPF records.
150 | print(domain,lookup)
151 | time.sleep(1)
152 | dnsCache[lookupKey] = lookup
153 | print("++[CACHE][" + domain + "] Added to DNS Cache - " + type)
154 | if countDepth == "on":
155 | depth += 1
156 | return lookup
157 | else:
158 | lookup = dnsCache[lookupKey]
159 | if depth == 0 and type=="TXT":
160 | for record in lookup:
161 | if record != None and re.match('^"v=spf1 ', record, re.IGNORECASE): # check if the first lookup record has a TXT SPF record.
162 | mydomains_source_success_status = True
163 |
164 | if mydomains_source_success_status == True: # using boolean, so as to only add 1 record (incase a domain has multiple v=spf1 records)
165 | mydomains_source_success.append(domain)
166 | else:
167 | mydomains_source_failure.append(domain) # has TXT record, but no SPF records.
168 | print(domain,lookup)
169 | time.sleep(1)
170 | if countDepth == "on":
171 | depth += 1
172 | cacheHit += 1
173 | print("==[CACHE][" + domain + "] Grabbed from DNS Cache - " + type)
174 | return lookup
175 |
176 | def getSPF(domain):
177 | global depth
178 | try:
179 | if depth == 0 and source_prefix_off == False:
180 | sourcerecord = source_prefix + "." + domain
181 | header.append("# Source of truth: " + sourcerecord)
182 | result = dnsLookup(sourcerecord,"TXT")
183 | elif depth == 0:
184 | header.append("# Source of truth: " + domain + " - Will not work in production unless you replace a single record. e.g. include:" + domain + " with include:%{ir}." + domain + "._spf.yourdomain.com")
185 | result = dnsLookup(domain,"TXT")
186 | else:
187 | result = dnsLookup(domain,"TXT")
188 |
189 | except:
190 | print("An exception occurred, check there is a DNS TXT record with SPF present at: " + str(source_prefix) + "." + str(domain) )
191 | if result:
192 | for record in result:
193 | if record != None and re.match('^"v=spf1 ', record, re.IGNORECASE):
194 | # replace " " with nothing which is used where TXT records exceed 255 characters
195 | record = record.replace("\" \"","")
196 | # remove " character from start and end
197 | spfvalue = record.replace("\"","")
198 | spfParts = spfvalue.split()
199 | header.append("# " + (paddingchar * depth) + " " + domain)
200 | header.append("# " + (paddingchar * depth) + " " + spfvalue)
201 |
202 | for spfPart in spfParts:
203 | if re.match('^[\+\-\~\?](all)$', spfPart, re.IGNORECASE):
204 | spfAction.append(spfPart)
205 | elif re.match('redirect=', spfPart, re.IGNORECASE):
206 | spfValue = spfPart.split('=')
207 | if spfValue[1] != domain and spfValue[1] and spfValue[1] not in includes:
208 | includes.append(spfValue[1])
209 | getSPF(spfValue[1])
210 | elif re.match('^(\+|)include\:', spfPart, re.IGNORECASE) and "%{" not in spfPart:
211 | spfValue = spfPart.split(':')
212 | if spfValue[1] != domain and spfValue[1] and spfValue[1] not in includes:
213 | includes.append(spfValue[1])
214 | getSPF(spfValue[1])
215 | elif spfValue[1]:
216 | error = "# ERROR DETECTED: Invalid DNS Record, Loop or Duplicate: " + spfValue[1] + " in " + domain
217 | header.append(error)
218 | print(error)
219 | elif re.match('^(\+|)ptr\:', spfPart, re.IGNORECASE):
220 | otherValues.append(spfPart)
221 | ipmonitor.append(spfPart)
222 | elif re.match('^(\+|)ptr', spfPart, re.IGNORECASE):
223 | otherValues.append(spfPart + ':' + domain)
224 | ipmonitor.append(spfPart + ':' + domain)
225 | elif re.match('^(\+|)a\:', spfPart, re.IGNORECASE):
226 | spfValue = spfPart.split(':')
227 | result = dnsLookup(spfValue[1],"A")
228 | result6 = dnsLookup(spfValue[1],"AAAA")
229 | if result:
230 | header.append("# " + (paddingchar * depth) + " " + spfPart)
231 | result = [(x + ' # a:' + spfValue[1]) for x in result]
232 | result.sort()
233 | for record in result:
234 | ip4.append(record)
235 | if result6:
236 | header.append("# " + (paddingchar * depth) + " " + spfPart)
237 | result6 = [(x + ' # aaaa:' + spfValue[1]) for x in result6]
238 | result6.sort() # sort
239 | for record in result6:
240 | ip6.append(record)
241 | elif re.match('^(\+|)a', spfPart, re.IGNORECASE):
242 | result = dnsLookup(domain,"A")
243 | result6 = dnsLookup(domain,"AAAA")
244 | if result:
245 | header.append("# " + (paddingchar * depth) + " " + spfPart + "(" + domain + ")")
246 | result = [x + " # a(" + domain + ")" for x in result]
247 | result.sort()
248 | for record in result:
249 | ip4.append(record)
250 | if result6:
251 | header.append("# " + (paddingchar * depth) + " " + spfPart + "(" + domain + ")")
252 | result6 = [x + " # aaaa(" + domain + ")" for x in result6]
253 | result6.sort()
254 | for record in result6:
255 | ip6.append(record)
256 | elif re.match('^(\+|)mx\:', spfPart, re.IGNORECASE):
257 | spfValue = spfPart.split(':')
258 | result = dnsLookup(spfValue[1],"MX")
259 | if result:
260 | header.append("# " + (paddingchar * depth) + " " + spfPart)
261 | mxrecords = []
262 | for mxrecord in result:
263 | mxValue = mxrecord.split(' ')
264 | mxrecords.append(mxValue[1])
265 | mxrecords.sort()
266 | for hostname in mxrecords:
267 | result = dnsLookup(hostname,"A","off")
268 | result6 = dnsLookup(hostname,"AAAA","off")
269 | if result:
270 | result = [x + ' # ' + spfPart + '=>a:' + hostname for x in result]
271 | result.sort()
272 | for record in result:
273 | ip4.append(record)
274 | header.append("# " + (paddingchar * depth) + " " + spfPart + "=>a:" + hostname)
275 | if result6:
276 | result6 = [x + ' # ' + spfPart + '=>aaaa:' + hostname for x in result6]
277 | result6.sort()
278 | for record in result6:
279 | ip6.append(record)
280 | header.append("# " + (paddingchar * depth) + " " + spfPart + "=>aaaa:" + hostname)
281 | elif re.match('^(\+|)mx', spfPart, re.IGNORECASE):
282 | result = dnsLookup(domain,"MX")
283 | if result:
284 | header.append("# " + (paddingchar * depth) + " mx(" + domain + ")")
285 | mxrecords = []
286 | for mxrecord in result:
287 | mxValue = mxrecord.split(' ')
288 | mxrecords.append(mxValue[1])
289 | mxrecords.sort()
290 | for hostname in mxrecords:
291 | result = dnsLookup(hostname,"A","off")
292 | result6 = dnsLookup(hostname,"AAAA","off")
293 | if result:
294 | result = [x + ' # mx(' + domain + ')=>a:' + hostname for x in result ]
295 | result.sort()
296 | for record in result:
297 | ip4.append(record)
298 | header.append("# " + (paddingchar * depth) + " mx(" + domain + ")=>a:" + hostname)
299 | if result6:
300 | result6 = [x + ' # mx(' + domain + ')=>aaaa:' + hostname for x in result6 ]
301 | result6.sort()
302 | for record in result6:
303 | ip6.append(record)
304 | header.append("# " + (paddingchar * depth) + " mx(" + domain + ")=>aaaa:" + hostname)
305 |
306 | elif re.match('^(\+|)ip4\:', spfPart, re.IGNORECASE):
307 | spfValue = re.split("ip4:", spfPart, flags=re.IGNORECASE)
308 | if spfValue[1] not in ipmonitor:
309 | if re.match('^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/([1-2][0-9]|[3][0-1])$',spfValue[1]): #later check IP against subnet and if present in subnet, ignore.
310 | ipmonitor.append(spfValue[1])
311 | ip4.append(spfValue[1] + " # subnet:" + domain)
312 | elif re.match('^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/32)?$',spfValue[1]): #later check IP against subnet and if present in subnet, ignore.
313 | ipmonitor.append(spfValue[1])
314 | ip4.append(spfValue[1] + " # ip:" + domain)
315 | else:
316 | ip4.append("# error:" + spfValue[1] + " for " + domain)
317 |
318 | else:
319 | header.append('# ' + (paddingchar * depth) + ' [Skipped] already added (ip4):' + spfValue[1] + " " + domain)
320 | elif re.match('(\+|)ip6\:', spfPart, re.IGNORECASE):
321 | spfValue = re.split("ip6:", spfPart, flags=re.IGNORECASE)
322 | if spfValue[1] not in ipmonitor:
323 | ipmonitor.append(spfValue[1])
324 | ip6.append(spfValue[1] + " # " + domain)
325 | else:
326 | header.append('# ' + (paddingchar * depth) + ' [Skipped] already added (ip6):' + spfValue[1] + " " + domain)
327 | elif re.match('v\=spf1', spfPart, re.IGNORECASE):
328 | spfValue = spfPart
329 | elif re.match('exists\:', spfPart, re.IGNORECASE) or re.match('include\:', spfPart, re.IGNORECASE):
330 | print('Added to fail response record:',spfPart)
331 | otherValues.append(spfPart)
332 | ipmonitor.append(spfPart)
333 | #else: drop everything else
334 |
335 |
336 | while totaldomaincount > 0:
337 | mydomains_source_success = []
338 | mydomains_source_failure = []
339 | dnsCache = {}
340 | loopcount += 1
341 | cacheHit = 0
342 | changeDetected = 0
343 | if restdb_url != None:
344 | try:
345 | mydomains = restdb(restdb_url,restdb_key)
346 | except:
347 | print("Error: restdb connection")
348 | else:
349 | totaldomaincount = len(mydomains)
350 | if runningconfigon == 1:
351 | runningconfig = []
352 | runningconfig = runningconfig + xpg8logo
353 | runningconfig.append("# Running config for: " + str(totaldomaincount) + ' domains' )
354 | runningconfig.append("# Source domains: " + ', '.join(mydomains))
355 | runningconfig.append("#\n#")
356 | start_time = time.time()
357 | print('Scanning SPF Records for domains: ' + str(mydomains))
358 | domaincount = 0
359 | for domain in mydomains:
360 | domaincount +=1
361 | datetimeNow = datetime.now(tz=None)
362 | headersummary = "# Automatically generated rbldnsd config by Expurgate[xpg8.tk] for:" + domain + " @ " + str(datetimeNow)
363 | header = []
364 | if runningconfigon == 1:
365 | header.append(headersummary)
366 | else:
367 | header = header + xpg8logo
368 | header.append(headersummary)
369 | ip4 = []
370 | allIp = []
371 | ip4header = []
372 | ip6 = []
373 | ip6header = []
374 | spfAction = []
375 | otherValues = []
376 | depth = 0
377 | includes = []
378 | ipmonitor = []
379 | stdoutprefix = '[' + str(loopcount) + ": " + str(domaincount) + '/' + str(totaldomaincount) + '][' + domain + "] "
380 | print(stdoutprefix + 'Looking up SPF records.')
381 | getSPF(domain)
382 |
383 |
384 |
385 | # strip spaces
386 | ip4 = [x.strip(' ') for x in ip4]
387 | ip6 = [x.strip(' ') for x in ip6]
388 |
389 | # CREATE ARRAYS FOR EACH PART OF THE RBLDNSD FILE
390 | header.append("# Depth:" + str(depth))
391 | # Set SPF Action
392 | if len(spfAction) > 0:
393 | spfActionValue = spfAction[0]
394 | #header.append("# SPF Cache Hits:" + str(cacheHit))
395 | ip4header.append("$DATASET ip4set:"+ domain +" " + domain)
396 |
397 | ip4header.append(":3:v=spf1 ip4:$ " + spfActionValue)
398 | if len(otherValues) > 0:
399 | therValues = list(dict.fromkeys(otherValues)) #dedupe
400 | ip4block = [":99:v=spf1 " + ' '.join(otherValues) + " " + spfActionValue]
401 | ip6block = [":99:v=spf1 " + ' '.join(otherValues) + " " + spfActionValue]
402 | else:
403 | ip4block = [":99:v=spf1 " + spfActionValue]
404 | ip6block = [":99:v=spf1 " + spfActionValue]
405 | ip4block.append("0.0.0.0/1 # all other IPv4 addresses")
406 | ip4block.append("128.0.0.0/1 # all other IP IPv4 addresses")
407 | ip6header.append("$DATASET ip6trie:"+ domain + " " + domain)
408 | ip6header.append(":3:v=spf1 ip6:$ " + spfActionValue)
409 | ip6block.append("0:0:0:0:0:0:0:0/0 # all other IPv6 addresses")
410 | allIp = ip4 + ip6
411 | header.append("# IP & Subnet: " + str(len(allIp)))
412 | ipmonitor.sort() # sort for comparison
413 | print(stdoutprefix + 'Comparing CURRENT and PREVIOUS record for changes :' + domain)
414 | if domain not in ipmonitorCompare:
415 | ipmonitorCompare[domain] = ipmonitor
416 | changeDetected += 1
417 | print(stdoutprefix + 'Change detected - First run, or a domain has only just been added.')
418 | elif ipmonitor == ipmonitorCompare[domain]:
419 | print(stdoutprefix + 'No change detected')
420 | else:
421 | changeDetected += 1
422 | print(stdoutprefix + 'Change detected!')
423 | print(stdoutprefix + 'Previous Record: ' + str(ipmonitorCompare[domain]))
424 | print(stdoutprefix + 'New Record: ' + str(ipmonitor))
425 | ipmonitorCompare[domain] = ipmonitor
426 |
427 | # Join all the pieces together, ready for file output
428 | myrbldnsdconfig = header + ip4header + list(set(ip4)) + ip4block + ip6header + list(set(ip6)) + ip6block # convert ip4 and ip6 to set to remove duplicates.
429 | if runningconfigon == 1:
430 | runningconfig = runningconfig + myrbldnsdconfig
431 |
432 | if changeDetected > 0 and runningconfigon == 0 and domaincount == len(mydomains_source_success):
433 | totalChangeCount += 1
434 | # Write the RBLDNSD config file to disk
435 | src_path = r'output/'+ domain.replace(".","-")+".staging"
436 | dst_path = r'output/'+ domain.replace(".","-")
437 | write2disk(src_path,dst_path,myrbldnsdconfig)
438 | lastChangeTime = strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
439 | elif domaincount != len(mydomains_source_success) and runningconfigon == 0:
440 | print(f"ERROR: {len(mydomains_source_success)} out of {domaincount} of your domains in MY_DOMAINS resolved successfully.")
441 | print("ERROR: Ensure each domain in MY_DOMAINS has a valid SPF record setup at SOURCE_PREFIX.")
442 | print("ERROR: No config file written, ensure internet and dns connectivity is working")
443 | print("ERROR: SPF TXT records requiring attention:",len(mydomains_source_failure),"-", str(mydomains_source_failure))
444 | elif runningconfigon == 0:
445 | print("Changes:",changeDetected,"mydomains:",len(mydomains),"mydomains_source_success:",len(mydomains_source_success),'mydomains_source_failure',mydomains_source_failure)
446 | print(f"No issues & no changes detected - No file written. Last change: {lastChangeTime})")
447 | print(stdoutprefix + 'Required ' + str(depth) + ' lookups.')
448 | if runningconfigon == 1:
449 | if changeDetected > 0 and len(mydomains) == len(mydomains_source_success):
450 | totalChangeCount += 1
451 | src_path = r'output/runningconfig.staging'
452 | dst_path = r'output/running-config'
453 | write2disk(src_path,dst_path,runningconfig)
454 | lastChangeTime = strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
455 | elif len(mydomains_source_failure) > 0:
456 | print(f"ERROR: {len(mydomains_source_success)} out of {len(mydomains)} of your domains in MY_DOMAINS resolved successfully.")
457 | print("ERROR: Ensure each domain in MY_DOMAINS has a valid SPF record setup at SOURCE_PREFIX.")
458 | print("ERROR: No config file written, ensure internet and dns connectivity is working")
459 | print("ERROR: SPF TXT records requiring attention:",len(mydomains_source_failure),"-", str(mydomains_source_failure))
460 | else:
461 | print("Changes:",changeDetected,"mydomains:",len(mydomains),"mydomains_source_success:",len(mydomains_source_success),'mydomains_source_failure',mydomains_source_failure)
462 | print(f"No issues & no changes detected - No file written. Last change: {lastChangeTime}")
463 | print("MODE: Running Config")
464 | else:
465 | print("MODE: Per Domain Config")
466 | end_time = time.time()
467 | time_lapsed = (end_time - start_time)
468 | print('Time Lapsed (seconds):' + str(math.ceil(time_lapsed)))
469 | if uptimekumapushurl != None:
470 | time_lapsed = time_lapsed * 1000 # calculate loop runtime and convert from seconds to milliseconds
471 | print("Pushing Uptime Kuma - endpoint : " + uptimekumapushurl + str(math.ceil(time_lapsed)))
472 | uptimeKumaPush(uptimekumapushurl + str(math.ceil(time_lapsed)))
473 | dnsReqTotal = len(dnsCache) + cacheHit
474 | if dnsReqTotal > 0:
475 | print(strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + " | Total Requests:" + str(dnsReqTotal) + " | DNS Cache Size:" + str(len(dnsCache)) + " | DNS Cache Hits:" + str(cacheHit) + " | DNS Cache vs Total:" + str(math.ceil((cacheHit/dnsReqTotal)*100)) + "%")
476 | else:
477 | print(strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + " | Total Requests:" + str(dnsReqTotal) + " | DNS Cache Size:" + str(len(dnsCache)) + " | DNS Cache Hits:" + str(cacheHit))
478 | print("Total Changes:" + str(totalChangeCount) + " | Last Change:" + lastChangeTime)
479 | print("Waiting " + str(delayBetweenRun) + " seconds before running again... ")
480 | sleep(int(delayBetweenRun)) # wait DELAY in secondsbefore running again.
--------------------------------------------------------------------------------
/expurgate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smck83/expurgate/da971d2068489f50ac3b6eba410145824f804e7c/expurgate.png
--------------------------------------------------------------------------------
/expurgate.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smck83/expurgate/da971d2068489f50ac3b6eba410145824f804e7c/expurgate.pptx
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | # install script for standalone debian host, or Amazon Lightsail Debian instance.
2 |
3 |
4 | mkdir -p /opt/expurgate/ && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl gnupg2 software-properties-common && \
5 | curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg --yes && \
6 | echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list && \
7 | apt-get update && apt-get install -y --no-install-recommends docker-ce docker-ce-cli containerd.io
8 |
--------------------------------------------------------------------------------
/python-vs-pypy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smck83/expurgate/da971d2068489f50ac3b6eba410145824f804e7c/python-vs-pypy.png
--------------------------------------------------------------------------------