This tutorial provides a complete, production-ready guide to integrating the Mark as Junk plugin in Roundcube Webmail with SpamAssassin (sa-learn) on a DirectAdmin server (compatible with DirectAdmin v1.677+ CustomBuild changes). Using sudoers permissions for the webapps user ensures that Bayes training updates the correct per-user SpamAssassin database securely.
Step 1: Create the Main Learning Script
Create the master script that receives the email payload from Roundcube and passes it to sa-learn under the specific DirectAdmin user context:
cat > /usr/local/bin/rc-sa-learn.sh << 'SCRIPT'
#!/bin/bash
TYPE="$1"
DOMAIN="$2"
MSGFILE="$3"
DAUSER=$(grep -i "^${DOMAIN}:" /etc/virtual/domainowners 2>/dev/null | awk -F': ' '{print $2}' | tr -d '[:space:]')
if [ -z "$DAUSER" ]; then
echo "$(date): ERROR - could not resolve DA user for domain $DOMAIN" >> /var/log/rc-sa-learn.log
exit 1
fi
BAYESPATH="/home/${DAUSER}/.spamassassin/bayes"
if [ "$TYPE" = "spam" ]; then
sudo -u "$DAUSER" /usr/bin/sa-learn --spam --dbpath="$BAYESPATH" "$MSGFILE" >> /var/log/rc-sa-learn.log 2>&1
elif [ "$TYPE" = "ham" ]; then
sudo -u "$DAUSER" /usr/bin/sa-learn --ham --dbpath="$BAYESPATH" "$MSGFILE" >> /var/log/rc-sa-learn.log 2>&1
fi
if [ $? -ne 0 ]; then
echo "$(date): WARNING - sa-learn failed for user $DAUSER (missing sudoers entry? run /root/bootstrap-sa-learn-sudoers.sh)" >> /var/log/rc-sa-learn.log
fi
SCRIPT
chmod +x /usr/local/bin/rc-sa-learn.sh
touch /var/log/rc-sa-learn.log
chown webapps:webapps /var/log/rc-sa-learn.log
chmod 640 /var/log/rc-sa-learn.log
Step 2: Automate Sudoers Rules for New Users (DirectAdmin Hook)
Set up a DirectAdmin post-user-creation script to automatically grant the webapps user passwordless sudo permissions for running sa-learn under newly created accounts:
mkdir -p /usr/local/directadmin/scripts/custom
cat > /usr/local/directadmin/scripts/custom/user_create_post.sh << 'SCRIPT'
#!/bin/sh
DAUSER="${username}"
if [ -z "$DAUSER" ]; then
exit 0
fi
SUDOFILE="/etc/sudoers.d/sa-learn-${DAUSER}"
if [ ! -f "$SUDOFILE" ]; then
cat > "$SUDOFILE" << EOF
webapps ALL=(${DAUSER}) NOPASSWD: /usr/bin/sa-learn --spam --dbpath=/home/${DAUSER}/.spamassassin/bayes *
webapps ALL=(${DAUSER}) NOPASSWD: /usr/bin/sa-learn --ham --dbpath=/home/${DAUSER}/.spamassassin/bayes *
EOF
chmod 440 "$SUDOFILE"
visudo -cf "$SUDOFILE" >/dev/null 2>&1 || rm -f "$SUDOFILE"
fi
exit 0;
SCRIPT
chmod 700 /usr/local/directadmin/scripts/custom/user_create_post.sh
chown diradmin:diradmin /usr/local/directadmin/scripts/custom/user_create_post.sh
Step 3: Provision Sudoers Rules for Existing Users
Create and execute a bootstrap script to generate the appropriate sudoers entries for all existing DirectAdmin users:
cat > /root/bootstrap-sa-learn-sudoers.sh << 'SCRIPT'
#!/bin/bash
while IFS=': ' read -r DOMAIN DAUSER; do
DAUSER=$(echo "$DAUSER" | tr -d '[:space:]')
[ -z "$DAUSER" ] && continue
SUDOFILE="/etc/sudoers.d/sa-learn-${DAUSER}"
if [ ! -f "$SUDOFILE" ]; then
cat > "$SUDOFILE" << EOF
webapps ALL=(${DAUSER}) NOPASSWD: /usr/bin/sa-learn --spam --dbpath=/home/${DAUSER}/.spamassassin/bayes *
webapps ALL=(${DAUSER}) NOPASSWD: /usr/bin/sa-learn --ham --dbpath=/home/${DAUSER}/.spamassassin/bayes *
EOF
chmod 440 "$SUDOFILE"
visudo -cf "$SUDOFILE" >/dev/null 2>&1 && echo "Provisioned: $DAUSER" || { rm -f "$SUDOFILE"; echo "FAILED (invalid): $DAUSER"; }
fi
done < /etc/virtual/domainowners
SCRIPT
chmod +x /root/bootstrap-sa-learn-sudoers.sh
/root/bootstrap-sa-learn-sudoers.sh
Verify that the rules were created successfully:
ls /etc/sudoers.d/ | grep sa-learn
Step 4: CustomBuild Roundcube Customization Setup (DA 1.677+)
Copy configuration files from the new CustomBuild exposed directory (./configure/roundcube/) to the custom overrides directory (./custom/roundcube/):
# Create custom directories
mkdir -p /usr/local/directadmin/custombuild/custom/roundcube/plugins
# Copy default config file template
cp /usr/local/directadmin/custombuild/configure/roundcube/config.inc.php /usr/local/directadmin/custombuild/custom/roundcube/config.inc.php
# Enable markasjunk in active plugins list
RCPLUGINCONFIG="/usr/local/directadmin/custombuild/custom/roundcube/config.inc.php"
if ! grep -qE "['\"]markasjunk['\"]" "$RCPLUGINCONFIG"; then
sed -i -E "s|(\\\$config\['plugins'\]\s*=\s*\[[^]]*)\]|\1, 'markasjunk']|" "$RCPLUGINCONFIG"
fi
grep -E "plugins" "$RCPLUGINCONFIG"
# Copy markasjunk plugin configuration to custom directory
mkdir -p /usr/local/directadmin/custombuild/custom/roundcube/plugins/markasjunk
cp /usr/local/directadmin/custombuild/configure/roundcube/plugins/markasjunk/config.inc.php.dist \
/usr/local/directadmin/custombuild/custom/roundcube/plugins/markasjunk/config.inc.php
# Configure markasjunk plugin driver and commands
sed -i \
-e "s|\$config\['markasjunk_learning_driver'\] = null;|\$config['markasjunk_learning_driver'] = 'cmd_learn';|" \
-e "s|\$config\['markasjunk_spam_cmd'\] = null;|\$config['markasjunk_spam_cmd'] = '/usr/local/bin/rc-sa-learn.sh spam %d %f';|" \
-e "s|\$config\['markasjunk_ham_cmd'\] = null;|\$config['markasjunk_ham_cmd'] = '/usr/local/bin/rc-sa-learn.sh ham %d %f';|" \
-e "s|\$config\['markasjunk_debug'\] = false;|\$config['markasjunk_debug'] = true;|" \
/usr/local/directadmin/custombuild/custom/roundcube/plugins/markasjunk/config.inc.php
Confirm the plugin configuration parameters:
grep -E "learning_driver|spam_cmd|ham_cmd|debug" /usr/local/directadmin/custombuild/custom/roundcube/plugins/markasjunk/config.inc.php
Step 5: Rebuild Roundcube and Restart Services
Rebuild Roundcube using CustomBuild to apply all custom options:
cd /usr/local/directadmin/custombuild
./build roundcube
systemctl restart spamd
da build exim_conf
Step 6: Verification and Logs
- Log into Roundcube Webmail interface.
- Select an email and click Mark as Junk (or As Not Junk).
- Check the log output to confirm successful processing:
tail -20 /var/log/rc-sa-learn.log
Maintenance: Future Upgrade Workflow
When upgrading Roundcube via CustomBuild in DirectAdmin 1.677+, compare your custom options against the default options exposed in configure/roundcube:
# Check main configuration differences
diff /usr/local/directadmin/custombuild/custom/roundcube/config.inc.php /usr/local/directadmin/custombuild/configure/roundcube/config.inc.php
# Check plugin configuration differences
diff /usr/local/directadmin/custombuild/custom/roundcube/plugins/markasjunk/config.inc.php /usr/local/directadmin/custombuild/configure/roundcube/plugins/markasjunk/config.inc.php.dist
If new configuration options appear in the default files after an update, copy and adjust them within your custom/roundcube files.
