SELinux: Dovecot IMAPSieve Spam Training Blocked (sa-learn execmem + labels)¶
On a Fedora/RHEL mail server with SELinux enforcing, moving a message into your Junk folder (to train spam) can silently fail — Bayes never learns the message, and an AVC-denial notice fires. The cause is that Dovecot's IMAPSieve learn pipeline runs sa-learn in the dovecot_t domain, which lacks several permissions by default.
Symptoms¶
- You drag/move a message into Junk (or out of Junk to train ham) and Bayes token counts don't change (
sa-learn --dump magicshows the samenspam/nham). - One or more
selinux-avc-notify(orsetroubleshoot) emails after a manual spam-learn session. ausearch -m avcshowsdovecot_tdenials such as:
Why It Happens¶
The training path is: Dovecot IMAPSieve pipe :copy → a shell wrapper (sa-learn-blocked-spam.sh / sa-learn-unblocked-ham.sh) → /usr/sbin/sa-learn. The wrapper scripts are labeled bin_t, and executing a bin_t file does not cause a domain transition — so sa-learn (and any helper like grep) runs as dovecot_t, the Dovecot service domain. That domain isn't granted everything a Perl-based learner needs:
| Denial | Needed by | Correct fix |
|---|---|---|
execute on the wrapper scripts |
Dovecot executing the pipe script | Label the scripts bin_t (not dovecot_etc_t) |
self:process execmem |
sa-learn / Perl (executable memory) |
Small local policy module (allow) |
open/write on the learn log |
Wrapper appending its log | Label the log dovecot_var_log_t |
Two of the three are fixed by relabeling, not by policy allows — keep the policy delta as small as possible.
Fix¶
1. Label the pipe scripts bin_t¶
semanage fcontext -a -t bin_t '/usr/lib/dovecot/sieve-pipe(/.*)?\.sh'
restorecon -Rv /usr/lib/dovecot/sieve-pipe
ls -Z /usr/lib/dovecot/sieve-pipe/*.sh # expect bin_t
This label drifts back to dovecot_etc_t after some updates/relabels
If it reverts, dovecot_t can no longer execute the scripts and training silently stops. Persist the semanage fcontext rule (above) so a restorecon restores bin_t rather than reverting it.
2. Label the learn log dovecot_var_log_t¶
The base Dovecot policy already lets dovecot_t write files of type dovecot_var_log_t, so relabeling avoids granting the overly-broad var_log_t:file open:
semanage fcontext -a -t dovecot_var_log_t '/var/log/dovecot-imapsieve-learn\.log'
restorecon -v /var/log/dovecot-imapsieve-learn.log
3. Grant execmem with a minimal local module¶
execmem cannot be fixed by relabeling — it's a process permission. Keep it scoped to exactly this:
cat > /etc/selinux/local/local_dovecot_salearn.te <<'EOF'
module local_dovecot_salearn 1.0;
require {
type dovecot_t;
class process execmem;
}
allow dovecot_t self:process execmem;
EOF
cd /etc/selinux/local
checkmodule -M -m -o local_dovecot_salearn.mod local_dovecot_salearn.te
semodule_package -o local_dovecot_salearn.pp -m local_dovecot_salearn.mod
semodule -i local_dovecot_salearn.pp
Why not just audit2allow -M the whole batch?
Piping all accumulated denials through audit2allow produces allow dovecot_t dovecot_etc_t:file execute; and allow dovecot_t var_log_t:file open; too — both far broader than needed. Relabel for those two and reserve the policy module for execmem only.
Verify¶
# Permissions now present:
sesearch -A -s dovecot_t -c process -p execmem | grep -w dovecot_t
sesearch -A -s dovecot_t -t dovecot_var_log_t -c file -p write
# End-to-end (the real test): move ONE spam message into Junk, then:
sa-learn --dump magic | grep -E 'nspam|nham' # count should increment
tail -2 /var/log/dovecot-imapsieve-learn.log # "SPAM learned score=..."
ausearch -m avc -ts recent | grep dovecot_t # expect none
Don't verify with runcon
Trying to runcon -t dovecot_t -- ...the-script... from a root/unconfined shell triggers a process transition denial (and its own AVC email) — that's a limitation of forcing the domain from outside, not a real failure. Only a genuine client-side Junk move exercises the true path.
Related¶
- SELinux: Fixing Fail2ban grep execmem Denial — the same
execmemclass for a different daemon. - SELinux: Fixing Dovecot Mail Spool Context (
/var/vmail) — the mailstore-labeling counterpart.