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

ivenae

Well-Known Member
Feb 11, 2022
136
56
48
42
Google operates several services that are commonly abused for sending spam. The following sections describe each service and the filtering strategies used.

Firebasemail / Firebaseapp​

Firebase is a Google-hosted platform that is frequently exploited for sending unsolicited email.

  • Emails sent directly through Firebase use the domain firebaseapp.com and can be blocked by sender domain.
  • Some senders use Firebase with their own custom domains, making direct domain blocking ineffective. However, these senders can still be identified early: their domain's SPF record (DNS TXT) contains _spf.firebasemail.com. This allows rejection before the SMTP DATA command, conserving server resources and preventing spam from entering the queue.

Google Groups​

Google Groups provides no opt-in or opt-out mechanism, making it trivially abusable: a spammer can add millions of recipients to a group and broadcast spam to all of them at once.

  • The sender domain can be any arbitrary registered domain, making domain-based filtering unreliable.
  • A Google Groups-specific header is present in each message, but inspecting it requires waiting for the full email data to be transmitted.
  • Fortunately, the envelope sender address always matches a predictable regular expression, which allows filtering before the DATA command is issued — the approach used by this policy daemon.

Google Usercontent​

googleusercontent.com is a Google server that, in practice, is a source of spam exclusively. Connections from this host are blocked at the client level.

Google Workspace & Gmail​

This filter intentionally does not block legitimate Google-based senders, including:

  • Google Workspace customers (e.g. ryanair.com, doctolib.com, willidrop.com)
  • Personal Gmail accounts (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}\+bncB[A-Z0-9]{25,}@',
]
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
A separate version of this filter maintains a local blacklist derived from historical mail data. A domain is automatically blacklisted if it meets the following criteria:

  • At least 8 messages received within a 14-day window
  • At least 80% of those messages were blocked

Exclusions & Whitelisting​

To prevent database poisoning and false positives, certain rejections are deliberately excluded from the statistical analysis:

  • SPF failures are ignored — rejected mails with SPF errors are likely spoofed and would corrupt the dataset.
  • DNSBL rejections are ignored — these typically involve spoofed sender domains, and since the rejection occurs before the SMTP connection is established, no spoof-proof sender verification is available.
The following sources are always whitelisted and will never be blacklisted automatically:

  • All .de domains — whitelisted for safety reasons. The source code can be modified to whitelist a different TLD if needed.
  • A curated list of known newsletter providers and other important services — to prevent legitimate bulk senders from being blocked accidentally.

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 ====
 

Attachments

Last edited:
  • Like
Reactions: Johannes S