<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs]]></title><description><![CDATA[<p dir="auto">Running three Cloudron servers (9.2.x, mail app 3.18.2, SpamAssassin 4.0.0) with<br />
about 35 mailboxes between them. While hardening against a run of phishing that<br />
was getting through, I went looking at how SpamAssassin is set up inside the mail<br />
container and found a few things that I think are worth sharing — plus some<br />
questions I couldn't answer from the docs.</p>
<p dir="auto">Everything below is reproducible with <code>docker exec mail …</code> on the host.</p>
<h3>1. The optional plugins are off — but that's upstream, not Cloudron</h3>
<p dir="auto">My first assumption was that Cloudron had disabled them. It hasn't:</p>
<pre><code>docker exec mail dpkg -V spamassassin
??5??????  c /etc/spamassassin/local.cf
??5??????  c /etc/spamassassin/v310.pre
</code></pre>
<p dir="auto">Only <code>local.cf</code> and <code>v310.pre</code> differ from the Debian package. <strong><code>v342.pre</code> is<br />
byte-identical</strong>, and that is the file holding <code>Phishing</code>, <code>FromNameSpoof</code>,<br />
<code>DecodeShortURLs</code>, <code>URILocalBL</code> and <code>PhishTag</code> — all commented out exactly as<br />
upstream ships them. So this is stock SpamAssassin behaviour, and I want to be<br />
clear I'm not reporting a Cloudron bug here. It only becomes a Cloudron question<br />
because of point 2.</p>
<p dir="auto">For completeness, Cloudron's entire spamd setup is this, from <code>/app/code/start.sh</code>:</p>
<pre><code class="language-bash">setup_spamd() {
    mkdir -p /run/spamd /app/data/spamd /run/spamd/tmp
    touch /app/data/spamd/custom.cf /app/data/spamd/acl.cf
    chown -R cloudron:cloudron /app/data/spamd /run/spamd
}
</code></pre>
<h3>2. <code>loadplugin</code> works from <code>custom.cf</code>, but the stock rules don't come with it</h3>
<p dir="auto">The only persistent config surface is the <strong>Custom SpamAssassin rules</strong> field in<br />
the Email view, which is <code>/app/data/spamd/custom.cf</code>, <code>include</code>d at the <em>end</em> of<br />
<code>local.cf</code>. Anything in <code>/etc/spamassassin/</code> is reset on app update.</p>
<p dir="auto"><code>loadplugin</code> is accepted there and lints clean:</p>
<pre><code>docker exec mail spamassassin --lint --cf="loadplugin Mail::SpamAssassin::Plugin::FromNameSpoof"
# exit 0
</code></pre>
<p dir="auto">But it has no effect on the plugin's own rules. Those live in<br />
<code>/usr/share/spamassassin/72_active.cf</code> behind an <code>ifplugin</code> guard, and that file<br />
is parsed <strong>before</strong> <code>local.cf</code> pulls in <code>custom.cf</code> — so by the time the plugin<br />
loads, the guard has already been evaluated and the rules skipped for good.</p>
<p dir="auto">Verified with a message whose <code>From:</code> display name is the recipient's address<br />
(should trip <code>T_FROMNAME_EQUALS_TO</code><img src="https://forum.cloudron.io/assets/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=ab6e4cc1e83" class="not-responsive emoji emoji-android emoji--disappointed" style="height:23px;width:auto;vertical-align:middle" title="):" alt="😞" /></p>
<pre><code># without the plugin
tests=FROM_2_EMAILS_SHORT,…            score=3.0

# with --cf="loadplugin …FromNameSpoof"
tests=FROM_2_EMAILS_SHORT,…            score=3.0     ← identical
</code></pre>
<p dir="auto"><strong>Workaround that does work:</strong> load the plugin <em>and</em> redefine the rules yourself<br />
in <code>custom.cf</code> under your own names. That gives 3.0 → 4.9 on the same message:</p>
<pre><code>loadplugin Mail::SpamAssassin::Plugin::FromNameSpoof

header   __MY_FROMNAME_SPOOF       eval:check_fromname_spoof()
header   __MY_FROMNAME_EQUALS_TO   eval:check_fromname_equals_to()
meta     MY_FROMNAME_SPOOFED_EMAIL (__MY_FROMNAME_SPOOF &amp;&amp; !__VIA_ML &amp;&amp; !__VIA_RESIGNER &amp;&amp; !__RP_MATCHES_RCVD)
meta     MY_FROMNAME_EQUALS_TO     __MY_FROMNAME_EQUALS_TO
score    MY_FROMNAME_SPOOFED_EMAIL 0.01
score    MY_FROMNAME_EQUALS_TO     0.01
</code></pre>
<p dir="auto">It works, but you're maintaining a copy of upstream's rule definitions, which<br />
will silently drift when SpamAssassin changes them.</p>
<h3>3. <code>sa-update</code> never runs</h3>
<p dir="auto">This one surprised me more than the plugins:</p>
<pre><code>docker exec mail ls /var/lib/spamassassin/
sa-update-keys                                  ← only the keys, no rules dir

docker exec mail ls /etc/cron.d/                ← e2scrub_all, php
docker exec mail grep -rn sa-update /app/code/start.sh /etc/supervisor/   ← nothing
</code></pre>
<p dir="auto">No versioned rules directory means <code>sa-update</code> has never completed. The static<br />
rule set is therefore whatever the Debian package ships —<br />
<code>/usr/share/spamassassin/50_scores.cf</code> is dated <strong>2022-07-01</strong> on my servers.</p>
<p dir="auto">In practice this matters less than it sounds, because the adaptive parts are all<br />
still live: DNSBL/URIBL lookups are DNS queries and always current, Bayes trains<br />
itself, and <code>custom.cf</code> is mine. But rule updates also carry <em>rescored</em> weights<br />
from current corpora, and those never arrive.</p>
<p dir="auto">I can see why it isn't run: <code>/var/lib/spamassassin</code> isn't persistent, so any<br />
<code>sa-update</code> result is discarded on the next app update.</p>
<h3>4. Two smaller things</h3>
<ul>
<li>The <code>Phishing</code> plugin reads its feed file only in <code>finish_parsing_end</code>, i.e.<br />
once at spamd startup. Refreshing the feed on disk does nothing until<br />
<code>supervisorctl restart spamd</code>. I'm doing that from a host cron job; it works,<br />
but it feels like reaching around the platform.</li>
<li>Unrelated to Cloudron, but a time-saver for anyone else wiring up that plugin:<br />
<code>https://openphish.com/feed.txt</code> is now a 302 to a GitHub raw URL, so <code>curl</code><br />
without <code>-L</code> silently writes an HTML error page over your feed. And the<br />
<code>phishstats.info/phish_score.csv</code> URL in the plugin's own documentation<br />
currently returns 404.</li>
</ul>
<h3>Questions</h3>
<ol>
<li>Is there a supported way to load a SpamAssassin plugin early enough that its<br />
stock <code>ifplugin</code>-guarded rules activate? A persistent <code>.pre</code> — or a second<br />
textarea next to the custom-rules field that gets written to a <code>.pre</code> — would<br />
solve it without anyone having to copy upstream rule definitions.</li>
<li>Is not running <code>sa-update</code> a deliberate choice? If so, what's the reasoning,<br />
and is there a recommended way to keep the static rule set reasonably current<br />
given that <code>/var/lib/spamassassin</code> doesn't survive an app update?</li>
<li>Would you consider enabling <strong>FromNameSpoof</strong> by default? It needs no external<br />
service, no feed, and no configuration, and display-name spoofing ("Rabobank"<br />
from a random domain) is one of the most common phishing shapes we see.</li>
<li>For external data files that a plugin reads at startup (like the phishing<br />
feed): is a host cron doing <code>docker exec mail supervisorctl restart spamd</code> the<br />
intended approach, or is there a cleaner hook?</li>
</ol>
<p dir="auto">Happy to test anything on my side — I have three servers and a corpus of real<br />
phishing that got through, so I can measure rather than guess.</p>
]]></description><link>https://forum.cloudron.io/topic/15746/mail-app-optional-spamassassin-plugins-can-t-be-enabled-with-their-stock-rules-and-sa-update-never-runs</link><generator>RSS for Node</generator><lastBuildDate>Sat, 01 Aug 2026 13:30:03 GMT</lastBuildDate><atom:link href="https://forum.cloudron.io/topic/15746.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 29 Jul 2026 07:07:13 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Thu, 30 Jul 2026 08:06:53 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/d19dotca" aria-label="Profile: d19dotca">@<bdi>d19dotca</bdi></a> both the statements are orthogonal. Cloudron can provide feature to tackle spam and make it more accessible for all users. At the same time, Cloudron can also not expose implementation details like SpamAssassin configs. In fact, there are newer solutions out there like say rspamd which even help with integrating email with LLM. But because Cloudron exposed SA we are now "stuck" and possibly have to break support at some point.</p>
<p dir="auto">We have always taken this approach to our product, so it's not anything new. We don't expose docker configs, haraka configs, dovecot configs, nginx configs etc. It's not how we do things. The SpamAssasin config was done only because an immediate problem had to be fixed and was done in haste.</p>
]]></description><link>https://forum.cloudron.io/post/127455</link><guid isPermaLink="true">https://forum.cloudron.io/post/127455</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Thu, 30 Jul 2026 08:06:53 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Thu, 30 Jul 2026 06:29:30 GMT]]></title><description><![CDATA[<blockquote>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a> <a href="/post/127390">said</a>:</p>
<p dir="auto">Also, the plan is not to expose spamassassin and it's features/plugins. We will simply integrate the use cases and never implementation details. Currently, the custom SA config is allowed in the UI but this was a mistake on our side.</p>
</blockquote>
<p dir="auto">Hmm, I am not sure if I agree with that view. Why do you think that was a mistake? If it wasn't customizable, many of us would be getting many more times the spam we already do because the default SpamAssassin rules are pretty awful these days with the aggressive spam nature as a result of so many more bots and more sophisticated spam attacks.</p>
<p dir="auto">Please don't remove that. If anything, I am in agreement with <a class="plugin-mentions-user plugin-mentions-a" href="/user/imc67" aria-label="Profile: imc67">@<bdi>imc67</bdi></a> that we need <em>more</em> controls. Cloudron is maturing, and with the help of AI many people are getting more advanced in their skillsets, and hiding things away from technical admins only increase friction with using Cloudron. I do hope that this will be reconsidered, and that more controls can be exposed for us.</p>
<hr />
<p dir="auto">On a separate note about the SpamAssassin sa-update and sa-learn stuff that Cloudron has a script for, I was recently looking into that myself and comparing it with a script I wrote myself for running some SpamAssassin scans for learning ham and spam (BAYES training). Here is an AI summary of this for reference:</p>
<pre><code>The bundled /app/code/spamassassin-config/learn-from-user-mail.sh, scheduled daily at 22:00 UTC by /app/code/service.js, only identifies paths under .Spam as spam.

Its ham search excludes exact .Drafts, .Spam, and .Trash paths, but it does not correctly handle:

- .Junk or nested Junk folders, which are learned as ham
- .Deleted Messages, which is learned as ham
- nested Trash folders such as .Trash.CANCELLATIONS
- Sent/Sent Messages, with no option for administrators to exclude them
- folder and message paths containing spaces, because results from find are processed through whitespace-splitting shell variables
- messages above the default spamc 512 KB limit

The script also applies head -n 200 to unsorted find output, so the selected messages are not necessarily the newest 200.

Could the learner use Dovecot special-use metadata (\Junk, \Trash, \Sent, and \Drafts) or, at minimum, robust Maildir folder-name matching with NUL-delimited paths?

It would also be helpful to make Sent-folder learning configurable and provide an administrator option to disable or customize the bundled automatic learner.
</code></pre>
]]></description><link>https://forum.cloudron.io/post/127449</link><guid isPermaLink="true">https://forum.cloudron.io/post/127449</guid><dc:creator><![CDATA[d19dotca]]></dc:creator><pubDate>Thu, 30 Jul 2026 06:29:30 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 14:16:49 GMT]]></title><description><![CDATA[<p dir="auto">It's back now</p>
]]></description><link>https://forum.cloudron.io/post/127410</link><guid isPermaLink="true">https://forum.cloudron.io/post/127410</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Wed, 29 Jul 2026 14:16:49 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 14:10:03 GMT]]></title><description><![CDATA[<blockquote>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a> <a href="/post/127398">said</a>:<br />
Currently, our gitlab is under attack. Almost 40k unique IPs. I took the IPs from our access.log and not some random blocklist.</p>
</blockquote>
<p dir="auto">oh that explains the downtime it had these days lol</p>
]]></description><link>https://forum.cloudron.io/post/127409</link><guid isPermaLink="true">https://forum.cloudron.io/post/127409</guid><dc:creator><![CDATA[andreasdueren]]></dc:creator><pubDate>Wed, 29 Jul 2026 14:10:03 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 09:02:06 GMT]]></title><description><![CDATA[<p dir="auto">haha (sorry) now you experience the same as I do, every hour the newly to be blocked IP's are pushed via API and it takes 4-6 minutes to be processed. That's why I suggested previously in a post to maken it "insert/update/delete" per IP or CIDR.</p>
<p dir="auto">Good luck!</p>
]]></description><link>https://forum.cloudron.io/post/127403</link><guid isPermaLink="true">https://forum.cloudron.io/post/127403</guid><dc:creator><![CDATA[imc67]]></dc:creator><pubDate>Wed, 29 Jul 2026 09:02:06 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 08:26:05 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/imc67" aria-label="Profile: imc67">@<bdi>imc67</bdi></a> wow <img src="https://forum.cloudron.io/assets/plugins/nodebb-plugin-emoji/emoji/android/1f642.png?v=ab6e4cc1e83" class="not-responsive emoji emoji-android emoji--slightly_smiling_face" style="height:23px;width:auto;vertical-align:middle" title=":)" alt="🙂" /> thing is in some of our servers even the hash list of the kernel is slow when adding so many IPs !</p>
<p dir="auto">(Sorry, for pulling down <a href="http://git.cloudron.io" target="_blank" rel="noopener noreferrer nofollow ugc">git.cloudron.io</a>, I hope to unblock everything later in the day but package updates requires gitlab to be stable)</p>
]]></description><link>https://forum.cloudron.io/post/127402</link><guid isPermaLink="true">https://forum.cloudron.io/post/127402</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Wed, 29 Jul 2026 08:26:05 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 08:07:32 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a> indeed I noticed <a href="http://git.cloudron.io" target="_blank" rel="noopener noreferrer nofollow ugc">git.cloudron.io</a> gave a 403.</p>
<p dir="auto">As you might remember I created my own "security monitor" for centrally monitoring and managing 3 Cloudron servers, it's growing with hundreds a day, the list below are lines, the unique IP's are 641.451.742 separate IPv4-adressess:<br />
<img src="/assets/uploads/files/1785312175312-scherm-afbeelding-2026-07-29-om-10.02.05.png" alt="Scherm­afbeelding 2026-07-29 om 10.02.05.png" class=" img-fluid img-markdown" /></p>
]]></description><link>https://forum.cloudron.io/post/127399</link><guid isPermaLink="true">https://forum.cloudron.io/post/127399</guid><dc:creator><![CDATA[imc67]]></dc:creator><pubDate>Wed, 29 Jul 2026 08:07:32 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 07:58:13 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/user/imc67" aria-label="Profile: imc67">@<bdi>imc67</bdi></a> no worries. But yeah fighting spam has become overly complicated and products don't exist yet to tackle this kind of scale.</p>
<p dir="auto">Currently, our gitlab is under attack. Almost 40k unique IPs. I took the IPs from our access.log and not some random blocklist. In fact, I blocked gitlab for everyone now and just put the company in a whitelist for the moment. I understand this is impractical for Email (our email too is under constant attack).</p>
<p dir="auto"><img src="/assets/uploads/files/1785311785648-353748d1-e075-4049-9c62-0e103c1efc90-image.jpeg" alt="353748d1-e075-4049-9c62-0e103c1efc90-image.jpeg" class=" img-fluid img-markdown" /></p>
]]></description><link>https://forum.cloudron.io/post/127398</link><guid isPermaLink="true">https://forum.cloudron.io/post/127398</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Wed, 29 Jul 2026 07:58:13 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 07:33:11 GMT]]></title><description><![CDATA[<p dir="auto">sorry <a class="plugin-mentions-user plugin-mentions-a" href="/user/girish" aria-label="Profile: girish">@<bdi>girish</bdi></a> it was indeed written by Claude Code Opus 5. But realise that in this AI world I noticed on my 3 Cloudron-servers a huge increase of attacks, spam and phishing (and the last one was once "successful" for one user with nasty results).</p>
<p dir="auto">As far as I can see the default "anti-spam / anti-phishing" in Cloudron is absolutely not sufficient, <a class="plugin-mentions-user plugin-mentions-a" href="/user/d19dotca" aria-label="Profile: d19dotca">@<bdi>d19dotca</bdi></a> was "the pioneer" here on the forum with custom SA rules and I fight "attackers with AI" with my own "AI" <img src="https://forum.cloudron.io/assets/plugins/nodebb-plugin-emoji/emoji/android/1f603.png?v=ab6e4cc1e83" class="not-responsive emoji emoji-android emoji--smiley" style="height:23px;width:auto;vertical-align:middle" title="😃" alt="😃" />.</p>
<p dir="auto">The only thing I ask you devs is to increase attention for "attacks, spam and phishing" and tell me what I can do if you don't/can't.</p>
]]></description><link>https://forum.cloudron.io/post/127393</link><guid isPermaLink="true">https://forum.cloudron.io/post/127393</guid><dc:creator><![CDATA[imc67]]></dc:creator><pubDate>Wed, 29 Jul 2026 07:33:11 GMT</pubDate></item><item><title><![CDATA[Reply to Mail app: optional SpamAssassin plugins can't be enabled with their stock rules, and `sa-update` never runs on Wed, 29 Jul 2026 07:09:22 GMT]]></title><description><![CDATA[<p dir="auto">There's too much AI text here to investigate but sa-update is now run periodically. I fixed this recently.</p>
<p dir="auto">Also, the plan is not to expose spamassassin and it's features/plugins. We will simply integrate the use cases and never implementation details. Currently, the custom SA config is allowed in the UI but this was a mistake on our side.</p>
]]></description><link>https://forum.cloudron.io/post/127390</link><guid isPermaLink="true">https://forum.cloudron.io/post/127390</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Wed, 29 Jul 2026 07:09:22 GMT</pubDate></item></channel></rss>