[TUTORIAL] Block firebaseapp, firebase and google groups early (before DATA)

ivenae

Well-Known Member
Feb 11, 2022
136
56
48
42
Google sends a lot of spam via different services

Firebasemail / Firebaseapp

This is a service hosted by Google that are used to send spam.

- They send e-mail via their own domain firebaseapp.com

- Some senders use Firebase with their own custom domains, but you can still block them early: their domain’s TXT SPF record contains _spf.firebasemail.com. You can use this information to reject these emails before the DATA command, saving resources and avoiding spam in your queue.


Google Groups

Google Groups has neither a opt-in, nor an opt-out. A spammer just put a million receivers into a google group list and sends spam to the list.
The sender domain could be a random, registered domain. That makes it impossible to filter google groups via the sender-domain.
They send a specific google groups header, but to check for this header, you have to wait until the sender sends the email data.

Out luck: the sender address always matches a specific regex which could be used to filter out e-mails from googlegroups before the smtp data command is send.

The following programm implements a policy daemon for filtering google groups spam.

Google Usercontent

googleusercontent.com is a server from google sending only spam, so we block this connection client.


Google Workspace and gmail.com/googlemail.com

This script does not reject google workspace, eg. ryanair.com, doctolib.com, willidrop.com and others, nor does it reject gmail.com/googlemail.com



Code:
apt install python3-dnspython

cat > /usr/local/bin/policyguard.py
Code:
#!/usr/bin/env python3

import sys
import time
import re
import dns.resolver
from functools import lru_cache

MAX_SPF_DEPTH = 10
CACHE_SIZE = 4096
SPF_BLOCK_INCLUDE = ["_spf.firebasemail.com"]
SENDER_BLOCK_INCLUDE = ["firebaseapp.com"]
BLOCKLIST_CLIENTNAME = ["googleusercontent.com"]
SENDER_REGEX_PATTERNS = [
    r'^[a-z0-9]{1,3}\+bnc[A-Z0-9]{10,}@',
    # weitere Regexe hier hinzufuegen
]
SENDER_REGEXES = [re.compile(p) for p in SENDER_REGEX_PATTERNS]

resolver = dns.resolver.Resolver()
resolver.timeout = 2
resolver.lifetime = 3


def log(msg):
    sys.stderr.write(f"spf-policy: {msg}\n")
    sys.stderr.flush()


@lru_cache(maxsize=CACHE_SIZE)
def get_spf(domain):
    try:
        answers = resolver.resolve(domain, "TXT")
        for r in answers:
            txt = "".join([s.decode() for s in r.strings])
            if txt.startswith("v=spf1"):
                return txt
    except Exception:
        pass
    return None


def spf_contains_block(domain, depth=0, visited=None):
    if visited is None:
        visited = set()

    if depth > MAX_SPF_DEPTH:
        return False

    if domain in visited:
        return False

    visited.add(domain)

    spf = get_spf(domain)
    if not spf:
        return False

    parts = spf.split()

    for p in parts:
        if p.startswith("include:"):
            include = p.split(":", 1)[1]

            if include in SPF_BLOCK_INCLUDE:
                return True

            if spf_contains_block(include, depth + 1, visited):
                return True

    return False


def sender_matches_regex(sender):
    for regex in SENDER_REGEXES:
        if regex.match(sender):
            return True, regex.pattern
    return False, None


def handle_request(attrs):
    sender = attrs.get("sender", "")
    client_name = attrs.get("client_name", "").lower()

    # Block Clients
    if any(client_name == b or client_name.endswith("." + b) for b in BLOCKLIST_CLIENTNAME):
        log(f"blocked client hostname: {client_name}")
        return "reject reject for policy reasons [policyguard] [guc]"
   
    if sender:
        matched, pattern = sender_matches_regex(sender)
        if matched:
            log(f"blocked sender by regex ({pattern}): {sender}")
            return "reject reject for policy reasons [gg]"

    if "@" not in sender:
        return "dunno"

    domain = sender.split("@", 1)[1].lower()
    try:
        if any(domain == base or domain.endswith('.' + base) for base in SENDER_BLOCK_INCLUDE):
            log(f"blocked sender domain {domain}")
            return "reject reject for policy reasons [fbm]"
  
        elif spf_contains_block(domain):
            log(f"blocked sender domain {domain}")
            return "reject reject for policy reasons [fbs]"
    except Exception as e:
        log(f"error checking {domain}: {e}")

    return "dunno"


def main():
    while True:

        attrs = {}

        while True:
            line = sys.stdin.readline()

            if line == "":
                return

            line = line.strip()

            if not line:
                break

            if "=" in line:
                k, v = line.split("=", 1)
                attrs[k] = v

        if not attrs:
            continue

        action = handle_request(attrs)

        print(f"action={action}\n")
        sys.stdout.flush()


if __name__ == "__main__":
    main()

Don't forget to set execution bit:
Code:
chmod +x /usr/local/bin/policyguard.py

copy templates, if not already done:


Code:
mkdir -p /etc/pmg/templates/
cp /var/lib/pmg/templates/master.cf.in /etc/pmg/templates/
cp /var/lib/pmg/templates/main.cf.in /etc/pmg/templates/


Add the policy_service to the existing smtpd_sender_restrictions to main.cf.in:
Code:
smtpd_sender_restrictions =
        check_policy_service    unix:private/policygrd
Add the two lines to the bottom of the master.cf.in
Code:
policygrd unix  -       n       n       -       0       spawn
    user=nobody argv=/usr/local/bin/policyguard.py


pmgconfig sync --restart


How to test?

Connect to service:
Code:
apt install socat
socat - UNIX-CONNECT:/var/spool/postfix/private/policygrd

Enter address and an empty line:
Code:
sender=test@example.com
        <--- empty line

Output:
Code:
action=dunno

Enter firebasespammail
Code:
sender=fckw@babyamerica.com
        <--- empty line


Output:
Code:
firebase-spf-policy: blocked sender domain babyamerica.com
action=reject Rejected for policy reasons [fbs]


Enter google groups spam
Code:
sender=aw7+bncBDIYHYXXTENBBUE76TGQMGQEVOOGNYY@teamkurofune.com
        <--- empty line


Output:
Code:
spf-policy: blocked sender by regex (^[a-z0-9]{1,3}\+bnc[A-Z0-9]{10,}@): aw7+bncBDIYHYXXTENBBUE76TGQMGQEVOOGNYY@teamkurofune.com
action=reject reject for policy reasons [gg]
 

Attachments

  • ksnip_20260319-113935.png
    ksnip_20260319-113935.png
    304.4 KB · Views: 3
Last edited:
  • Like
Reactions: rct and section1
I have another version which also manages a local blacklist from history-data:
If a domain exist, which only sent spam (minimum 8 mails during 14 days with 80% blocked), we block the whole domain.

We ignore rejected mails with SPF failures, because we do not want to have spoofed fakemails poisen our database.
We also ignore rejected mails due to DNSBL, because they mostly use spoofed sender domains and we do not have any spoof-proof because of the early rejection.
".de" domains are whitelisted for security reason. you can change the sourcecode if you want to whitelist another TLD instead of ".de"
there is a whitelist with some newsletter sending domains to be sure they will not accidentially blocked.

create tempdir and run visudo

Code:
mkdir /p /t/
chmod -R 777 /t/
visudo -f /etc/sudoers.d/policyguard

allow journalctl to run as nobody
Code:
nobody ALL=(root) NOPASSWD: /usr/bin/journalctl


after connecting with socat you can enter "historystats" or current blocklist with "blocklist"
Code:
 socat - UNIX-CONNECT:/var/spool/postfix/private/policygrd
spf-policy: load_cache: 14 days loaded from /t/postfix_cache.json
spf-policy: initialize_cache: all historical days present in cache file
spf-policy: initialize_cache: loading today's stats from journal
blocklist
==== BEGIN CURRENT BLOCKLIST ====
acmbms.com
agenciatrendingmedia.com
arbentio.buzz
aristabd.com
artemishomeautomation.co.uk
babyamerica.com
businessworking.com.br
capturesoul.com
casang.vip
cidder.pro
com.tr
csyxzn.com
customyourproduct.com
dachser.com
datadecodedlabs.com
devalser.hair
domointelligence.com
edvaldoeliezer.com.br
elasticemail.net
eng-school.lol
firebaseapp.com
forest-host.beer
forest-host.living
fork-link.top
france-make.shop
freshservice.com
gan-gane.beauty
howtomeasurecobb.com
hubucoapp.com
ink-factory.fr
jmpghana.com
jonnhytech.com
ksmg.org
love-story.mom
mannativf.com
momentstudio.ca
murmansk.su
my-location.my
my.id
mybluehost.me
netnameprovider.cn
nikosale.makeup
novastek.homes
nxcli.io
org.es
qgui777com.com
ranchodasviolasfm.com.br
rksutar.com
rksuthar.com
rsgsv.net
samirengineering.com
sfp888.com
shop-ram.homes
somanex.company
soniafoodsnig.com
spiderm.click
stresspromo.com
sunecom.discount
sunmil.day
teamkurofune.com
temamillstore.com
texbangla.com
trianon-hotels.com
tugiveprod.autos
turbo-smtp.info
wftelecompi.com.br
winstells.my
wuyedongman.info
zaralokakalyanafoundation.org
zuoyoujixie.com
==== END CURRENT BLOCKLIST ====
historystats
==== TOTAL DOMAIN STATS (all days + today) ====
  firebaseapp.com                accept: 0  blocked: 130  total: 130  percent blocked: 100 %
  casang.vip                     accept: 0  blocked: 29  total: 29  percent blocked: 100 %
  novastek.homes                 accept: 0  blocked: 25  total: 25  percent blocked: 100 %
  spiderm.click                  accept: 0  blocked: 48  total: 48  percent blocked: 100 %
  wftelecompi.com.br             accept: 0  blocked: 23  total: 23  percent blocked: 100 %
  france-make.shop               accept: 0  blocked: 39  total: 39  percent blocked: 100 %
  com.tr                         accept: 0  blocked: 46  total: 46  percent blocked: 100 %
  eng-school.lol                 accept: 0  blocked: 21  total: 21  percent blocked: 100 %
  winstells.my                   accept: 0  blocked: 18  total: 18  percent blocked: 100 %
  somanex.company                accept: 0  blocked: 47  total: 47  percent blocked: 100 %
  stresspromo.com                accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  gan-gane.beauty                accept: 0  blocked: 32  total: 32  percent blocked: 100 %
  murmansk.su                    accept: 0  blocked: 19  total: 19  percent blocked: 100 %
  sunecom.discount               accept: 0  blocked: 39  total: 39  percent blocked: 100 %
  cidder.pro                     accept: 0  blocked: 12  total: 12  percent blocked: 100 %
  hubucoapp.com                  accept: 0  blocked: 28  total: 28  percent blocked: 100 %
  dachser.com                    accept: 0  blocked: 14  total: 14  percent blocked: 100 %
  sunmil.day                     accept: 0  blocked: 60  total: 60  percent blocked: 100 %
  trianon-hotels.com             accept: 0  blocked: 25  total: 25  percent blocked: 100 %
  elasticemail.net               accept: 0  blocked: 13  total: 13  percent blocked: 100 %
  turbo-smtp.info                accept: 0  blocked: 10  total: 10  percent blocked: 100 %
  tugiveprod.autos               accept: 0  blocked: 28  total: 28  percent blocked: 100 %
  forest-host.living             accept: 0  blocked: 47  total: 47  percent blocked: 100 %
  devalser.hair                  accept: 0  blocked: 30  total: 30  percent blocked: 100 %
  ranchodasviolasfm.com.br       accept: 0  blocked: 11  total: 11  percent blocked: 100 %
  shop-ram.homes                 accept: 0  blocked: 30  total: 30  percent blocked: 100 %
  mannativf.com                  accept: 0  blocked: 10  total: 10  percent blocked: 100 %
  howtomeasurecobb.com           accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  customyourproduct.com          accept: 0  blocked: 24  total: 24  percent blocked: 100 %
  datadecodedlabs.com            accept: 0  blocked: 11  total: 11  percent blocked: 100 %
  org.es                         accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  nikosale.makeup                accept: 0  blocked: 39  total: 39  percent blocked: 100 %
  fork-link.top                  accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  ksmg.org                       accept: 0  blocked: 17  total: 17  percent blocked: 100 %
  arbentio.buzz                  accept: 0  blocked: 14  total: 14  percent blocked: 100 %
  love-story.mom                 accept: 0  blocked: 11  total: 11  percent blocked: 100 %
  domointelligence.com           accept: 0  blocked: 18  total: 18  percent blocked: 100 %
  samirengineering.com           accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  my.id                          accept: 0  blocked: 14  total: 14  percent blocked: 100 %
  acmbms.com                     accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  rksuthar.com                   accept: 0  blocked: 10  total: 10  percent blocked: 100 %
  businessworking.com.br         accept: 0  blocked: 14  total: 14  percent blocked: 100 %
  edvaldoeliezer.com.br          accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  wuyedongman.info               accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  jonnhytech.com                 accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  mybluehost.me                  accept: 0  blocked: 13  total: 13  percent blocked: 100 %
  nxcli.io                       accept: 0  blocked: 21  total: 21  percent blocked: 100 %
  my-location.my                 accept: 0  blocked: 10  total: 10  percent blocked: 100 %
  ink-factory.fr                 accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  sfp888.com                     accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  freshservice.com               accept: 0  blocked: 14  total: 14  percent blocked: 100 %
  capturesoul.com                accept: 0  blocked: 190  total: 190  percent blocked: 100 %
  teamkurofune.com               accept: 0  blocked: 199  total: 199  percent blocked: 100 %
  forest-host.beer               accept: 0  blocked: 11  total: 11  percent blocked: 100 %
  zaralokakalyanafoundation.org  accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  csyxzn.com                     accept: 0  blocked: 10  total: 10  percent blocked: 100 %
  agenciatrendingmedia.com       accept: 0  blocked: 8  total: 8  percent blocked: 100 %
  netnameprovider.cn             accept: 0  blocked: 9  total: 9  percent blocked: 100 %
  zuoyoujixie.com                accept: 0  blocked: 36  total: 36  percent blocked: 100 %
  artemishomeautomation.co.uk    accept: 1  blocked: 33  total: 34  percent blocked: 97 %
  aristabd.com                   accept: 1  blocked: 15  total: 16  percent blocked: 93 %
  qgui777com.com                 accept: 1  blocked: 14  total: 15  percent blocked: 93 %
  momentstudio.ca                accept: 1  blocked: 11  total: 12  percent blocked: 91 %
  soniafoodsnig.com              accept: 1  blocked: 11  total: 12  percent blocked: 91 %
  rksutar.com                    accept: 1  blocked: 10  total: 11  percent blocked: 90 %
  temamillstore.com              accept: 2  blocked: 15  total: 17  percent blocked: 88 %
  rsgsv.net                      accept: 1  blocked: 7  total: 8  percent blocked: 87 %
  texbangla.com                  accept: 2  blocked: 12  total: 14  percent blocked: 85 %
  jmpghana.com                   accept: 4  blocked: 22  total: 26  percent blocked: 84 %
  babyamerica.com                accept: 4  blocked: 19  total: 23  percent blocked: 82 %
  thegreenfieldfzc.com           accept: 2  blocked: 6  total: 8  percent blocked: 75 %
  hubspotemail.net               accept: 4  blocked: 4  total: 8  percent blocked: 50 %
  premium-box.eu                 accept: 6  blocked: 4  total: 10  percent blocked: 40 %
  businesscooperation.eu         accept: 8  blocked: 5  total: 13  percent blocked: 38 %
  mailjet.com                    accept: 21  blocked: 8  total: 29  percent blocked: 27 %
  mailin.fr                      accept: 12  blocked: 3  total: 15  percent blocked: 20 %
==== END TOTAL DOMAIN STATS ====
 
Last edited:
  • Like
Reactions: Johannes S