停止使用 Spamhaus DNSBL

剛剛看到「If you query Spamhaus Projects’ legacy DNSBLs via DigitalOcean move to the free Data Query Service」這篇,覺得愈來愈詭異了,研究了目前的情況後決定停用 Spamhaus

現在已經愈來愈少自己架設 mail server 了,不過我自己還是留了幾個 domain 跑在自己架設的 Postfix 上面,最主要是 command line 下面用 Mutt 讀信還是蠻方便的,另外一方面是確保一個信箱是不受到大企業的管制。

如果不是拿套裝軟體直接架設的話,自己架設 mail server 會有不少東西要設定:在 MTA 這端通常會使用 DNSBL 擋掉已知會發 spam 的 IP address。

DNSBL 的原理不難,就是拿 IPv4 address 組合一個 hostname,透過 DNS 查詢就會知道這個 IPv4 address 是否在清單;換句話說,就是拿 DNS protocol 當作 API,當作資料庫查詢。

舉個例子來說,假設我要查 188.235.18.134 這個位置的情況 (從「Worst /24 blocks based on total spam count」這邊翻出來的),這邊使用 SpamCop 的清單,我先把 IPv4 address 反過來變成 134.18.235.188,然後再加上 SpamCop 所指定的 bl.spamcop.net,變成 134.18.235.188.bl.spamcop.net,接下來查詢就可以查到:

134.18.235.188.bl.spamcop.net has address 127.0.0.2

如果是 168.95.1.1 的話,同樣方法組合成 1.1.95.168.bl.spamcop.net 可以看到沒有在 SpamCop 清單內:

Host 1.1.95.168.bl.spamcop.net not found: 3(NXDOMAIN)

這邊選擇用 DNS 的好處包括了 DNS resolver 及 DNS library 自然的 cache,不需要 Postfix 這類 MTA 再自己實作 cache 層,對於有大量信件 (無論是正常的或是 spam) 進來的時候也不會造成提供清單的服務大量的負載。

回頭來說 Spamhaus 的情況,他們公告要擋 DigitalOcean 的理由很奇怪,因為 DigitalOcean 架設了自己的 mirror 所以他們不知道使用的量,要使用者去 Spamhaus 上註冊申請後拿到一個自己的 your_DQS_key.zen.dq.spamhaus.net 使用。

有了 unique key 在 query,這樣就給了 Spamhaus 很清晰追蹤資料,加上 Privacy Policy 裡面的資訊:

We may have to share your personal data with the parties set out below for the purposes set out in the table in paragraph 4.
[...]
– Third parties to whom we may choose to sell, transfer, or merge parts of our business or our assets. Alternatively, we may seek to acquire other businesses or merge with them. If a change happens to our business, then the new owners may use your personal data in the same way as set out in this privacy notice.

這樣就知道他們想要做什麼了...

另外一方面,查資料的時候發現他們已經擋掉 Google Public DNS 以及 Cloudflare DNS

這是我自己架設 Unbound 的查詢:

gslin@home [~] [05:09/W4] host 2.0.0.127.zen.spamhaus.org
2.0.0.127.zen.spamhaus.org has address 127.0.0.10
2.0.0.127.zen.spamhaus.org has address 127.0.0.4
2.0.0.127.zen.spamhaus.org has address 127.0.0.2

這是 Google Public DNS (8.8.8.8):

gslin@home [~] [05:09/W4] host 2.0.0.127.zen.spamhaus.org 8.8.8.8
Using domain server:
Name: 8.8.8.8
Address: 8.8.8.8#53
Aliases: 

Host 2.0.0.127.zen.spamhaus.org not found: 3(NXDOMAIN)

這是 Cloudflare DNS (1.1.1.1):

gslin@home [~] [05:09/W4] host 2.0.0.127.zen.spamhaus.org 1.1.1.1
Using domain server:
Name: 1.1.1.1
Address: 1.1.1.1#53
Aliases: 

2.0.0.127.zen.spamhaus.org has address 127.255.255.254

在 Spamhaus 的「Frequently Asked Questions (FAQ)」這篇裡面有提到 127.255.255.254 的回應是「Query via public/open resolver」:

127.255.255.252	Any	Typing error in DNSBL name
127.255.255.254	Any	Query via public/open resolver
127.255.255.255	Any	Excessive number of queries

所以還蠻清楚這家的東西已經不能碰了...

Google Groups 將在 2024/02/22 斷開與 Usenet 的連接

在 news.admin.peering 上看到的消息:「Effective February 15, 2024, Google Groups will no longer support new Usenet content」,在 Google Groups 上面也可以看到:

Effective February 15, 2024, Google Groups will no longer support new Usenet content. Posting and subscribing will be disallowed, and new content from Usenet peers will not appear. Viewing and searching of historical data will still be supported as it is done today.

而在 Hacker News 上也有討論「Google Groups ending support for Usenet (support.google.com)」。

話說先前寫的「Google Groups 的 spam 的量又下降了不少...」這篇,後來又有發現不是 Google Groups 的 spam 減少,而是 Cleanfeed 裡面用 SpamAssassin 會造成跑久以後會 100% CPU,反而接收的速度慢下來...

在上了 workaround 後 (每個小時自動重跑一次 innd),可以看到其實完全沒有變少,反而愈來愈多:

倒是後來一直丟 corpus 進去練 SpamAssassin 後看起來效果好很多了,還是會看到一些 spam,但就沒那麼多了...

Google Groups 的 spam 的量又下降了不少...

延續「從 Google Groups 送出來的 spam 數量稍微下降...」這邊的觀察,從 10/22 到 10/29 降了 2/3 左右,再往後拉七天到了 11/5 可以看到又少了 3/4 左右。

另外整個 article volume 看起來也有降很多,從三個不同的 peering 來的量都有降,看起來是從源頭就有做一些事情。

這是 10/22 的量:

這是 10/29 的量:

這是 11/5 的量:

從 Google Groups 送出來的 spam 數量稍微下降...

先前在「Google Groups 的巨量 spam」這邊提到從 Google Groups 倒進 usenet 大量的 spam,最近看起來稍微緩解了一些。

這是 10/22 的量:

這是 10/29 的量:

可以看出來整體被 Perl filter 擋下來的量大幅降低了,在 comp.lang.c 也可以看出來 10/28 後似乎暫時停了...?

只能繼續觀察看看了...

在 Cleanfeed 裡面用 Mail::SpamAssassin 的 Bayesian filter 來擋 Google Groups 的垃圾

本來單純用 Cleanfeed + Mail::SpamAssassin 擋,效果其實不太好:「Google Groups 的巨量 spam」。

後來在 news.software.nntp 討論區裡面有人提到應該要用 sa-learn 訓練:

OK, now you need a ~/.spamassassin directory for your news user and a user_prefs file in that directory. After that you can start adding rules for Usenet spam. You will also need to feed several hundreds of spam and ham articles to sa-learn --spam or sa-learn --ham as the news user. After that, SpamAssassin will gradually improve.

死馬當活馬醫看看,結果看起來效果就出來了:

累積了幾天下來後,單看跑進 comp.lang.c 裡面的 spam 與 Mail::SpamAssassin 這邊擋下來的量,差不多是 99%+ 的量,接下來就是有看到的部分再丟進去 train。

目前看起來唯一的問題就是 Google Groups 的 spam 量真的很大,導致 innd 因為跑 Mail::SpamAssassin 的 bayesian 運算時 CPU usage 會高不少,偶而會撞到 100%,但不是常態所以還好。

繼續觀察看看...

Google Groups 的巨量 spam

Google Groups 的 spam 使得 Usenet 上不少群組都受到波及了,像是 comp.lang.c 的 spam 量已經多到是每天上萬則在上面:

目前看起來 Cleanfeed 擋不住,我試著寫個小 hack (是還蠻 hack 的),在 cleanfeed.local 裡面用 SpamAssassin 過濾,效果也很不好:

目前最佳解應該就是直接擋掉 Google Groups,另外似乎是有人包裝好 NoCeM 出來,也許後續也可以看看...

Amazon SES 寄到 Gmail 受到阻擋的情況

我自己沒遇過,但是 Hacker News 上看到有人有遇到,所以記錄起來:「Tell HN: Gmail rate limiting emails from AWS SES」。

Amazon SES 預設是共用 IP pool,所以遇到這種情況不算太意外,但應該是暫時性的,不過發問的作者有提到後來的解法是花 US$25/mo 使用 Dedicated IP 解決 IP reputation 的問題 (在 id=37177533 這邊):

Thanks you all for comments. I have made a decision to subscribed to dedicated IPs (credits: @slau).

The differentiating factor between our current AWS SES plan and the competitors (mentioned in the comments) is having a dedicated IP. With our current volume, none of the competitors are anyway near AWS SES costs. So, moving to a dedicated IPs thats cost 25$ extra not only solves our issue, but also no change in code/infrastructure.

記得以前另外一個教訓是,寄信還是儘量用 IPv4 address 去寄,因為 IPv6 address 的 reputation 得養頗久... 不過這個也是很久前的事情了。

Fake GitHub Star 的生意

昨天在 Hacker News 首頁上看到「Tracking the Fake GitHub Star Black Market (dagster.io)」這篇,原文在「Tracking the Fake GitHub Star Black Market with Dagster, dbt and BigQuery」這邊。

作者群想要偵測 GitHub 上面 fake star 的行為,所以就跑去找黑市買,然後找到了兩家,Baddhi Shop (1000 個 $64) 與 GitHub24 (每個 €0.85,大約是 $0.91),價錢差異很大,「品質」差異也很大:貴的 star 在一個月後還是存在,而便宜的看起來有一些有被 GitHub 偵測到而清除掉:

A month later, all 100 GitHub24 stars still stood, but only three-quarters of the fake Baddhi Shop stars remained. We suspect the rest were purged by GitHub’s integrity teams.

接下來就是想要系統化分析,切入點是 GH Archive 這個服務,可以直接下載 GitHub 全站上的 public evnets 資料:

GH Archive is a project to record the public GitHub timeline, archive it, and make it easily accessible for further analysis.

想要偵測兩種不同的 fake account,第一種是 obvious fake account,定義成這樣:

  • Created in 2022 or later
  • Followers <=1
  • Following <= 1
  • Public gists == 0
  • Public repos <=4
  • Email, hireable, bio, blog, and twitter username are empty
  • Star date == account creation date == account updated date

從定義就可以看出來完全就是灌水帳號,開出來就沒在動的。從 screenshot 可以看出這種帳號長的都一樣:

另外一種則是透過演算法去分析,這邊拿 unsupervised clustering 類的演算法分析出來的結果,可以看到抓到很多:

最近 NN 類的 machine learning 演算法太多,看到這些傳統的 machine learning 演算法還是覺得頗新鮮的...

限制流量的方式 (rate limit)

Lobsters Daily 上看到這篇 2017 年的文章,Figma 的工程師講怎麼做 rate limit:「An alternative approach to rate limiting」,只要大一點的站台就會遇到 spammer 之類的攻擊,就會希望實做自動化的機制擋住 spammer。

文章裡面提到了三種方式,第一種 (類) 提到了經典的 Token bucketLeaky bucket,這邊文章提供的演算法是讓每個使用者都會有一筆資料紀錄在 Redis 裡面 (這邊的用法可以抽換換成 Memcached),裡面記錄了最後一次的 access time 以及還剩下多少 token 可以用,接下來就可以照時間計算 token 的補充與消耗:

但這個演算法的缺點是 race condition,需要另外設計一些機制確保操作的 atomic:

不過大多數的實做就算不管 atomic 也還行 OK,只是會比較不精確一點。

第二個方法他叫做 Fixed window counters,這個方法把時間切齊為單位 (像是 60 秒為一個 window),所以可以把起點的時間也放到 key 裡面,然後 value 就是數量:

這個作法的好處就是簡單,而且 Redis 與 Memcached 都有提供 atomic 的 +1 操作。但缺點是可能會發生兩倍以上的 request,像是 5 reqs/min 的限制有可能會有連續的一分鐘內達到 10 reqs/min:

不過我覺得就 antispam 來說算是夠用了,當年 (大概是 2007 或是 2008 年?) 在 PIXNET 時用 C 寫 Apache module 就是把資料丟到 Memcached 裡面就是這樣實做的,然後每次學術網路的實驗室跑來掃站的就會自動被擋 XDDD

第三種方式他們稱作 Sliding window log,就是把每個 request 的 timestamp 都存起來,這個部份用 Redis 的資料結構會比用 Memcached 方便一些:

這個方式在控制上更精確,不過空間成本上就高很多... 這樣算是把常見的實做方式都提到了。

這幾天 blog 被掃,用 nginx 的 limit_req_zone 擋...

Update:這個方法問題好像還是不少,目前先拿掉了...

這幾天 blog 被掃中單一頁面負載會比較重的頁面,結果 CPU loading 變超高,從後台可以看到常常滿載:

看了一下是都是從 Azure 上面打過來的,有好幾組都在打,IP address 每隔一段時間就會變,所以單純用 firewall 擋 IP address 的方法看起來沒用...

印象中 nginx 本身可以 rate limit,搜了一下文件可以翻到應該就是「Module ngx_http_limit_req_module」這個,就設起來暫時用這個方式擋著,大概是這樣:

limit_conn_status 429;
limit_req_status 429;
limit_req_zone $binary_remote_addr zone=myzone:10m rate=10r/m;

其中預設是傳回 5xx 系列的 service unavailable,但這邊用 429 應該更正確,從維基百科的「List of HTTP status codes」這邊可以看到不錯的說明:

429 Too Many Requests (RFC 6585)
The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes.

然後 virtual host 的設定檔內把某個 path 放進這個 zone 保護起來,目前比較困擾的是需要 copy & paste try_filesFastCGI 相關的設定:

    location /path/subpath {
        limit_req zone=myzone;
        try_files $uri $uri/ /index.php?$args;

        include fastcgi.conf;
        fastcgi_intercept_errors on;
        fastcgi_pass php74;
    }

這樣一來就可以自動擋下這些狂抽猛送的 bot,至少在現階段應該還是有用的...

如果之後有遇到其他手法的話,再見招拆招看看要怎麼再加強 :o