├── .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 | ![expurgate - simplify, hide and exceed SPF lookup limits](https://github.com/smck83/expurgate/blob/main/expurgate.png?raw=true) 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 | ![image](https://github.com/smck83/expurgate/blob/main/expurgate-diagram.png) 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 | ![image](https://github.com/smck83/expurgate/blob/main/python-vs-pypy.png) 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 --------------------------------------------------------------------------------