330 lines
No EOL
150 KiB
HTML
330 lines
No EOL
150 KiB
HTML
<!DOCTYPE html><html lang="en" dir="ltr" data-theme="dark" data-has-toc data-has-sidebar class="astro-bguv2lll"> <head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>Catching spam with rspamd | ISPmail Guide</title><link rel="canonical" href="https://workaround.org/ispmail-trixie/catching-spam-with-rspamd/"/><link rel="sitemap" href="/sitemap-index.xml"/><script src="https://rybbit.workaround.org/api/script.js" data-site-id="1" async defer></script><link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml"/><meta name="generator" content="Astro v5.16.6"/><meta name="generator" content="Starlight v0.35.3"/><meta property="og:title" content="Catching spam with rspamd"/><meta property="og:type" content="article"/><meta property="og:url" content="https://workaround.org/ispmail-trixie/catching-spam-with-rspamd/"/><meta property="og:locale" content="en"/><meta property="og:description"/><meta property="og:site_name" content="ISPmail Guide"/><meta name="twitter:card" content="summary_large_image"/><script>
|
||
window.StarlightThemeProvider = (() => {
|
||
const storedTheme =
|
||
typeof localStorage !== 'undefined' && localStorage.getItem('starlight-theme');
|
||
const theme =
|
||
storedTheme ||
|
||
(window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
|
||
document.documentElement.dataset.theme = theme === 'light' ? 'light' : 'dark';
|
||
return {
|
||
updatePickers(theme = storedTheme || 'auto') {
|
||
document.querySelectorAll('starlight-theme-select').forEach((picker) => {
|
||
const select = picker.querySelector('select');
|
||
if (select) select.value = theme;
|
||
/** @type {HTMLTemplateElement | null} */
|
||
const tmpl = document.querySelector(`#theme-icons`);
|
||
const newIcon = tmpl && tmpl.content.querySelector('.' + theme);
|
||
if (newIcon) {
|
||
const oldIcon = picker.querySelector('svg.label-icon');
|
||
if (oldIcon) {
|
||
oldIcon.replaceChildren(...newIcon.cloneNode(true).childNodes);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
};
|
||
})();
|
||
</script><template id="theme-icons"><svg aria-hidden="true" class="light astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M5 12a1 1 0 0 0-1-1H3a1 1 0 0 0 0 2h1a1 1 0 0 0 1-1Zm.64 5-.71.71a1 1 0 0 0 0 1.41 1 1 0 0 0 1.41 0l.71-.71A1 1 0 0 0 5.64 17ZM12 5a1 1 0 0 0 1-1V3a1 1 0 0 0-2 0v1a1 1 0 0 0 1 1Zm5.66 2.34a1 1 0 0 0 .7-.29l.71-.71a1 1 0 1 0-1.41-1.41l-.66.71a1 1 0 0 0 0 1.41 1 1 0 0 0 .66.29Zm-12-.29a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.71-.71a1.004 1.004 0 1 0-1.43 1.41l.73.71ZM21 11h-1a1 1 0 0 0 0 2h1a1 1 0 0 0 0-2Zm-2.64 6A1 1 0 0 0 17 18.36l.71.71a1 1 0 0 0 1.41 0 1 1 0 0 0 0-1.41l-.76-.66ZM12 6.5a5.5 5.5 0 1 0 5.5 5.5A5.51 5.51 0 0 0 12 6.5Zm0 9a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7Zm0 3.5a1 1 0 0 0-1 1v1a1 1 0 0 0 2 0v-1a1 1 0 0 0-1-1Z"/></svg><svg aria-hidden="true" class="dark astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.64 13a1 1 0 0 0-1.05-.14 8.049 8.049 0 0 1-3.37.73 8.15 8.15 0 0 1-8.14-8.1 8.59 8.59 0 0 1 .25-2A1 1 0 0 0 8 2.36a10.14 10.14 0 1 0 14 11.69 1 1 0 0 0-.36-1.05Zm-9.5 6.69A8.14 8.14 0 0 1 7.08 5.22v.27a10.15 10.15 0 0 0 10.14 10.14 9.784 9.784 0 0 0 2.1-.22 8.11 8.11 0 0 1-7.18 4.32v-.04Z"/></svg><svg aria-hidden="true" class="auto astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg></template><link rel="stylesheet" href="/_astro/print.DNXP8c50.css" media="print"><link rel="stylesheet" href="/_astro/index.DpSp7KkT.css">
|
||
<style>@layer starlight.components{:root{--sl-badge-default-border: var(--sl-color-accent);--sl-badge-default-bg: var(--sl-color-accent-low);--sl-badge-default-text: #fff;--sl-badge-note-border: var(--sl-color-blue);--sl-badge-note-bg: var(--sl-color-blue-low);--sl-badge-note-text: #fff;--sl-badge-danger-border: var(--sl-color-red);--sl-badge-danger-bg: var(--sl-color-red-low);--sl-badge-danger-text: #fff;--sl-badge-success-border: var(--sl-color-green);--sl-badge-success-bg: var(--sl-color-green-low);--sl-badge-success-text: #fff;--sl-badge-caution-border: var(--sl-color-orange);--sl-badge-caution-bg: var(--sl-color-orange-low);--sl-badge-caution-text: #fff;--sl-badge-tip-border: var(--sl-color-purple);--sl-badge-tip-bg: var(--sl-color-purple-low);--sl-badge-tip-text: #fff}[data-theme=light]:root{--sl-badge-default-bg: var(--sl-color-accent-high);--sl-badge-note-bg: var(--sl-color-blue-high);--sl-badge-danger-bg: var(--sl-color-red-high);--sl-badge-success-bg: var(--sl-color-green-high);--sl-badge-caution-bg: var(--sl-color-orange-high);--sl-badge-tip-bg: var(--sl-color-purple-high)}.sl-badge:where(.astro-avdet4wd){display:inline-block;border:1px solid var(--sl-color-border-badge);border-radius:.25rem;font-family:var(--sl-font-system-mono);line-height:normal;color:var(--sl-color-text-badge);background-color:var(--sl-color-bg-badge);overflow-wrap:anywhere}.sidebar-content .sl-badge:where(.astro-avdet4wd){line-height:1;font-size:var(--sl-text-xs);padding:.125rem .375rem}.sidebar-content a[aria-current=page]>.sl-badge:where(.astro-avdet4wd){--sl-color-bg-badge: transparent;--sl-color-border-badge: currentColor;color:inherit}.default:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-default-bg);--sl-color-border-badge: var(--sl-badge-default-border);--sl-color-text-badge: var(--sl-badge-default-text)}.note:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-note-bg);--sl-color-border-badge: var(--sl-badge-note-border);--sl-color-text-badge: var(--sl-badge-note-text)}.danger:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-danger-bg);--sl-color-border-badge: var(--sl-badge-danger-border);--sl-color-text-badge: var(--sl-badge-danger-text)}.success:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-success-bg);--sl-color-border-badge: var(--sl-badge-success-border);--sl-color-text-badge: var(--sl-badge-success-text)}.tip:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-tip-bg);--sl-color-border-badge: var(--sl-badge-tip-border);--sl-color-text-badge: var(--sl-badge-tip-text)}.caution:where(.astro-avdet4wd){--sl-color-bg-badge: var(--sl-badge-caution-bg);--sl-color-border-badge: var(--sl-badge-caution-border);--sl-color-text-badge: var(--sl-badge-caution-text)}.small:where(.astro-avdet4wd){font-size:var(--sl-text-xs);padding:.125rem .25rem}.medium:where(.astro-avdet4wd){font-size:var(--sl-text-sm);padding:.175rem .35rem}.large:where(.astro-avdet4wd){font-size:var(--sl-text-base);padding:.225rem .45rem}.sl-markdown-content :is(h1,h2,h3,h4,h5,h6) .sl-badge:where(.astro-avdet4wd){vertical-align:middle}}
|
||
@layer starlight.components{.card-grid:where(.astro-zntqmydn){display:grid;grid-template-columns:100%;gap:1rem}.card-grid:where(.astro-zntqmydn)>*{margin-top:0!important}@media(min-width:50rem){.card-grid:where(.astro-zntqmydn){grid-template-columns:1fr 1fr;gap:1.5rem}.stagger:where(.astro-zntqmydn){--stagger-height: 5rem;padding-bottom:var(--stagger-height)}.stagger:where(.astro-zntqmydn)>*:nth-child(2n){transform:translateY(var(--stagger-height))}}}
|
||
@layer starlight.components{.card:where(.astro-v5tidmuc){--sl-card-border: var(--sl-color-purple);--sl-card-bg: var(--sl-color-purple-low);border:1px solid var(--sl-color-gray-5);background-color:var(--sl-color-black);padding:clamp(1rem,calc(.125rem + 3vw),2.5rem);flex-direction:column;gap:clamp(.5rem,calc(.125rem + 1vw),1rem)}.card:where(.astro-v5tidmuc):nth-child(4n+1){--sl-card-border: var(--sl-color-orange);--sl-card-bg: var(--sl-color-orange-low)}.card:where(.astro-v5tidmuc):nth-child(4n+3){--sl-card-border: var(--sl-color-green);--sl-card-bg: var(--sl-color-green-low)}.card:where(.astro-v5tidmuc):nth-child(4n+4){--sl-card-border: var(--sl-color-red);--sl-card-bg: var(--sl-color-red-low)}.card:where(.astro-v5tidmuc):nth-child(4n+5){--sl-card-border: var(--sl-color-blue);--sl-card-bg: var(--sl-color-blue-low)}.title:where(.astro-v5tidmuc){font-weight:600;font-size:var(--sl-text-h4);color:var(--sl-color-white);line-height:var(--sl-line-height-headings);gap:1rem;align-items:center}.card:where(.astro-v5tidmuc) .icon:where(.astro-v5tidmuc){border:1px solid var(--sl-card-border);background-color:var(--sl-card-bg);padding:.2em;border-radius:.25rem;flex-shrink:0}.card:where(.astro-v5tidmuc) .body:where(.astro-v5tidmuc){margin:0;font-size:clamp(var(--sl-text-sm),calc(.5rem + 1vw),var(--sl-text-body))}}
|
||
@layer starlight.components{svg:where(.astro-c6vsoqas){color:var(--sl-icon-color);font-size:var(--sl-icon-size, 1em);width:1em;height:1em}}
|
||
@layer starlight.components{starlight-tabs:where(.astro-esqgolmp){display:block}.tablist-wrapper:where(.astro-esqgolmp){overflow-x:auto}:where(.astro-esqgolmp)[role=tablist]{display:flex;list-style:none;border-bottom:2px solid var(--sl-color-gray-5);padding:0}.tab:where(.astro-esqgolmp){margin-bottom:-2px}.tab:where(.astro-esqgolmp)>:where(.astro-esqgolmp)[role=tab]{display:flex;align-items:center;gap:.5rem;padding:0 1.25rem;text-decoration:none;border-bottom:2px solid var(--sl-color-gray-5);color:var(--sl-color-gray-3);outline-offset:var(--sl-outline-offset-inside);overflow-wrap:initial}.tab:where(.astro-esqgolmp) :where(.astro-esqgolmp)[role=tab][aria-selected=true]{color:var(--sl-color-white);border-color:var(--sl-color-text-accent);font-weight:600}.tablist-wrapper:where(.astro-esqgolmp)~[role=tabpanel]{margin-top:1rem}}
|
||
@layer starlight.components{.sl-steps{--bullet-size: calc(var(--sl-line-height) * 1rem);--bullet-margin: .375rem;list-style:none;counter-reset:steps-counter var(--sl-steps-start, 0);padding-inline-start:0}.sl-steps>li{counter-increment:steps-counter;position:relative;padding-inline-start:calc(var(--bullet-size) + 1rem);padding-bottom:1px;min-height:calc(var(--bullet-size) + var(--bullet-margin))}.sl-steps>li+li{margin-top:0}.sl-steps>li:before{content:counter(steps-counter);position:absolute;top:0;inset-inline-start:0;width:var(--bullet-size);height:var(--bullet-size);line-height:var(--bullet-size);font-size:var(--sl-text-xs);font-weight:600;text-align:center;color:var(--sl-color-white);background-color:var(--sl-color-gray-6);border-radius:99rem;box-shadow:inset 0 0 0 1px var(--sl-color-gray-5)}.sl-steps>li:after{--guide-width: 1px;content:"";position:absolute;top:calc(var(--bullet-size) + var(--bullet-margin));bottom:var(--bullet-margin);inset-inline-start:calc((var(--bullet-size) - var(--guide-width)) / 2);width:var(--guide-width);background-color:var(--sl-color-hairline-light)}}@layer starlight.content{.sl-steps>li>:first-child{--lh: calc(1em * var(--sl-line-height));--shift-y: calc(.5 * (var(--bullet-size) - var(--lh)));transform:translateY(var(--shift-y));margin-bottom:var(--shift-y)}.sl-steps>li>:first-child:where(h1,h2,h3,h4,h5,h6){--lh: calc(1em * var(--sl-line-height-headings))}@supports (--prop: 1lh){.sl-steps>li>:first-child{--lh: 1lh}}}
|
||
@layer starlight.components{starlight-file-tree:where(.astro-p67cqifm){--x-space: 1.5rem;--y-space: .125rem;--y-pad: 0;display:block;border:1px solid var(--sl-color-gray-5);padding:1rem;background-color:var(--sl-color-gray-6);font-size:var(--sl-text-xs);font-family:var(--__sl-font-mono);overflow-x:auto}starlight-file-tree:where(.astro-p67cqifm) .directory>details{border:0;padding:0;padding-inline-start:var(--x-space);background:transparent}starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary{margin-inline-start:calc(-1 * var(--x-space));border:0;padding:var(--y-pad) .625rem;font-weight:400;color:var(--sl-color-white);max-width:100%}starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary::marker,starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary::-webkit-details-marker{color:var(--sl-color-gray-3)}starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary:hover,starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary:hover .tree-icon{cursor:pointer;color:var(--sl-color-text-accent);fill:currentColor}starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary:hover~ul{border-color:var(--sl-color-gray-4)}starlight-file-tree:where(.astro-p67cqifm) .directory>details>summary:hover .highlight .tree-icon{color:var(--sl-color-text-invert);fill:currentColor}starlight-file-tree:where(.astro-p67cqifm) ul{margin-inline-start:.5rem;border-inline-start:1px solid var(--sl-color-gray-5);padding:0;padding-inline-start:.125rem;list-style:none}starlight-file-tree:where(.astro-p67cqifm)>ul{margin:0;border:0;padding:0}starlight-file-tree:where(.astro-p67cqifm) li{margin:var(--y-space) 0;padding:var(--y-pad) 0}starlight-file-tree:where(.astro-p67cqifm) .file{margin-inline-start:calc(var(--x-space) - .125rem);color:var(--sl-color-white)}starlight-file-tree:where(.astro-p67cqifm) .tree-entry{display:inline-flex;align-items:flex-start;flex-wrap:wrap;max-width:calc(100% - 1rem)}@media(min-width:30em){starlight-file-tree:where(.astro-p67cqifm) .tree-entry{flex-wrap:nowrap}}starlight-file-tree:where(.astro-p67cqifm) .tree-entry>:first-child{flex-shrink:0}starlight-file-tree:where(.astro-p67cqifm) .empty{color:var(--sl-color-gray-3);padding-inline-start:.375rem}starlight-file-tree:where(.astro-p67cqifm) .comment{color:var(--sl-color-gray-3);padding-inline-start:1.625rem;max-width:24rem;min-width:12rem}starlight-file-tree:where(.astro-p67cqifm) .highlight{display:inline-block;border-radius:.25rem;padding-inline-end:.5rem;color:var(--sl-color-text-invert);background-color:var(--sl-color-text-accent)}starlight-file-tree:where(.astro-p67cqifm) svg{display:inline;fill:var(--sl-color-gray-3);vertical-align:middle;margin-inline:.25rem .375rem;width:.875rem;height:.875rem}starlight-file-tree:where(.astro-p67cqifm) .highlight svg.tree-icon{fill:currentColor}}
|
||
@layer starlight.components{.sl-link-button:where(.astro-xwgiixxa){align-items:center;border:1px solid transparent;border-radius:999rem;display:inline-flex;font-size:var(--sl-text-sm);gap:.5em;line-height:1.1875;outline-offset:.25rem;padding:.4375rem 1.125rem;text-decoration:none}.sl-link-button:where(.astro-xwgiixxa).primary{background:var(--sl-color-text-accent);border-color:var(--sl-color-text-accent);color:var(--sl-color-black)}.sl-link-button:where(.astro-xwgiixxa).primary:hover{color:var(--sl-color-black)}.sl-link-button:where(.astro-xwgiixxa).secondary{border-color:inherit;color:var(--sl-color-white)}.sl-link-button:where(.astro-xwgiixxa).minimal{color:var(--sl-color-white);padding-inline:0}.sl-link-button:where(.astro-xwgiixxa) svg{flex-shrink:0}@media(min-width:50rem){.sl-link-button:where(.astro-xwgiixxa){font-size:var(--sl-text-base);padding:.9375rem 1.25rem}}.sl-markdown-content .sl-link-button:where(.astro-xwgiixxa){margin-inline-end:1rem}.sl-markdown-content .sl-link-button:where(.astro-xwgiixxa):not(:where(p *)){margin-block:1rem}}
|
||
</style><script type="module" src="/_astro/page.B1D-nYk3.js"></script></head> <body class="astro-bguv2lll"> <a href="#_top" class="astro-7q3lir66">Skip to content</a> <div class="page sl-flex astro-vrdttmbt"> <header class="header astro-vrdttmbt"><div class="header astro-kmkmnagf"> <div class="title-wrapper sl-flex astro-kmkmnagf"> <a href="/" class="site-title sl-flex astro-m46x6ez3"> <img class="light:sl-hidden print:hidden astro-m46x6ez3" alt src="/_astro/logo-dark.DlBk_-6-.svg" width="146" height="37"> <img class="dark:sl-hidden print:block astro-m46x6ez3" alt src="/_astro/logo.DfR9nmxN.svg" width="146" height="37"> <span class="sr-only astro-m46x6ez3" translate="no"> ISPmail Guide </span> </a> </div> <div class="sl-flex print:hidden astro-kmkmnagf"> <site-search class="astro-kmkmnagf astro-v37mnknz" data-translations="{"placeholder":"Search"}"> <button data-open-modal disabled aria-label="Search" aria-keyshortcuts="Control+K" class="astro-v37mnknz"> <svg aria-hidden="true" class="astro-v37mnknz astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21.71 20.29 18 16.61A9 9 0 1 0 16.61 18l3.68 3.68a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.39ZM11 18a7 7 0 1 1 0-14 7 7 0 0 1 0 14Z"/></svg> <span class="sl-hidden md:sl-block astro-v37mnknz" aria-hidden="true">Search</span> <kbd class="sl-hidden md:sl-flex astro-v37mnknz" style="display: none;"> <kbd class="astro-v37mnknz">Ctrl</kbd><kbd class="astro-v37mnknz">K</kbd> </kbd> </button> <dialog style="padding:0" aria-label="Search" class="astro-v37mnknz"> <div class="dialog-frame sl-flex astro-v37mnknz"> <button data-close-modal class="sl-flex md:sl-hidden astro-v37mnknz"> Cancel </button> <div class="search-container astro-v37mnknz"> <div id="starlight__search" class="astro-v37mnknz"></div> </div> </div> </dialog> </site-search> <script>
|
||
(() => {
|
||
const openBtn = document.querySelector('button[data-open-modal]');
|
||
const shortcut = openBtn?.querySelector('kbd');
|
||
if (!openBtn || !(shortcut instanceof HTMLElement)) return;
|
||
const platformKey = shortcut.querySelector('kbd');
|
||
if (platformKey && /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform)) {
|
||
platformKey.textContent = '⌘';
|
||
openBtn.setAttribute('aria-keyshortcuts', 'Meta+K');
|
||
}
|
||
shortcut.style.display = '';
|
||
})();
|
||
</script> <script type="module" src="/_astro/Search.astro_astro_type_script_index_0_lang.cjYDvRdi.js"></script> </div> <div class="sl-hidden md:sl-flex print:hidden right-group astro-kmkmnagf"> <div class="sl-flex social-icons astro-kmkmnagf"> <a href="https://git.workaround.org/chaas/ispmail-workaround-org" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Git</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M0.355 12.231L0.355 12.231L0.355 11.853Q0.397 11.517 0.565 11.265L0.565 11.265Q0.691 11.097 0.943 10.803L0.943 10.803L1.279 10.425L7.831 3.831L7.873 3.873Q7.957 3.873 7.957 3.957L7.957 3.957L10.309 6.351Q10.519 6.519 10.309 6.729L10.309 6.729Q10.225 7.317 10.435 7.863Q10.645 8.409 11.107 8.703L11.107 8.703Q11.275 8.787 11.317 8.892Q11.359 8.997 11.359 9.207L11.359 9.207L11.359 15.003Q11.359 15.255 11.107 15.507L11.107 15.507Q10.603 15.843 10.372 16.389Q10.141 16.935 10.330 17.502Q10.519 18.069 10.981 18.447Q11.443 18.825 12.031 18.825Q12.619 18.825 13.081 18.489Q13.543 18.153 13.732 17.586Q13.921 17.019 13.753 16.473Q13.585 15.927 13.081 15.507L13.081 15.507Q12.913 15.423 12.871 15.318Q12.829 15.213 12.829 15.003L12.829 15.003L12.829 9.081L12.955 9.081L15.055 11.181Q15.139 11.265 15.139 11.391L15.139 11.391L15.181 11.475L15.181 12.231Q15.265 13.029 15.874 13.470Q16.483 13.911 17.281 13.806Q18.079 13.701 18.562 13.029Q19.045 12.357 18.835 11.601L18.835 11.601Q18.751 10.929 18.100 10.488Q17.449 10.047 16.735 10.131L16.735 10.131Q16.483 10.131 16.231 10.005L16.231 10.005L14.005 7.779Q13.879 7.653 13.879 7.401L13.879 7.401Q14.005 6.813 13.711 6.267Q13.417 5.721 12.871 5.448Q12.325 5.175 11.779 5.301L11.779 5.301Q11.359 5.301 11.359 5.175L11.359 5.175Q9.805 3.579 9.007 2.907L9.007 2.907Q8.797 2.739 9.007 2.529L9.007 2.529Q9.469 2.193 10.183 1.353L10.183 1.353L10.729 0.807Q11.905-0.369 13.081 0.807L13.081 0.807L23.035 10.803Q24.253 11.979 23.035 13.155L23.035 13.155L13.291 22.941Q12.913 23.319 12.745 23.445L12.745 23.445Q12.409 23.739 12.031 23.781L12.031 23.781L11.779 23.781L11.569 23.697Q11.065 23.487 10.855 23.277L10.855 23.277L10.057 22.479Q8.881 21.387 8.335 20.757L8.335 20.757L1.153 13.575Q1.069 13.365 0.775 13.029L0.775 13.029Q0.397 12.525 0.355 12.231Z"/></svg></a><a href="https://riot.im/app/#/room/#ispmail:matrix.org" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Matrix</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M22.5 1.5v21h-2.25V24H24V0h-3.75v1.5h2.25ZM7.46 7.95V9.1h.04a3.02 3.02 0 0 1 2.61-1.39c.54 0 1.03.1 1.48.32.44.2.78.58 1.01 1.1.26-.37.6-.7 1.03-.99.44-.28.95-.43 1.55-.43.45 0 .87.06 1.26.17.38.11.71.29.99.53.27.24.49.56.64.95.15.4.23.86.23 1.42v5.72h-2.34v-4.85c0-.29-.01-.56-.04-.8a1.73 1.73 0 0 0-.18-.67 1.1 1.1 0 0 0-.44-.45 1.6 1.6 0 0 0-.78-.16c-.33 0-.6.06-.8.19-.2.12-.37.29-.48.5a2 2 0 0 0-.23.69c-.04.26-.06.52-.06.78v4.77H10.6v-4.8l-.01-.75a2.29 2.29 0 0 0-.14-.69c-.08-.2-.23-.38-.42-.5a1.5 1.5 0 0 0-.85-.2c-.15.01-.3.04-.44.08-.19.06-.37.15-.52.28-.18.14-.32.34-.44.6-.12.26-.18.6-.18 1.02v4.96H5.25V7.94h2.21ZM1.5 1.5v21h2.25V24H0V0h3.75v1.5H1.5Z"/></svg></a><a href="https://comentario.workaround.org/api/rss/comments?domain=0f111a27-fbfa-48af-8beb-ab12e612d92f" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Feed</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M2.88 16.88a3 3 0 0 0 0 4.24 3 3 0 0 0 4.24 0 3 3 0 0 0-4.24-4.24Zm2.83 2.83a1 1 0 0 1-1.42-1.42 1 1 0 0 1 1.42 0 1 1 0 0 1 0 1.42ZM5 12a1 1 0 0 0 0 2 5 5 0 0 1 5 5 1 1 0 0 0 2 0 7 7 0 0 0-7-7Zm0-4a1 1 0 0 0 0 2 9 9 0 0 1 9 9 1 1 0 0 0 2 0 11.08 11.08 0 0 0-3.22-7.78A11.08 11.08 0 0 0 5 8Zm10.61.39A15.11 15.11 0 0 0 5 4a1 1 0 0 0 0 2 13 13 0 0 1 13 13 1 1 0 0 0 2 0 15.11 15.11 0 0 0-4.39-10.61Z"/></svg></a> </div> <starlight-theme-select> <label style="--sl-select-width: 6.25em" class="astro-4yphtoen"> <span class="sr-only astro-4yphtoen">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-4yphtoen astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg> <select autocomplete="off" class="astro-4yphtoen"> <option value="dark" class="astro-4yphtoen">Dark</option><option value="light" class="astro-4yphtoen">Light</option><option value="auto" selected class="astro-4yphtoen">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-4yphtoen astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg> </label> </starlight-theme-select> <script>
|
||
StarlightThemeProvider.updatePickers();
|
||
</script> <script type="module">const r="starlight-theme",o=e=>e==="auto"||e==="dark"||e==="light"?e:"auto",c=()=>o(typeof localStorage<"u"&&localStorage.getItem(r));function n(e){typeof localStorage<"u"&&localStorage.setItem(r,e==="light"||e==="dark"?e:"")}const l=()=>matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";function t(e){StarlightThemeProvider.updatePickers(e),document.documentElement.dataset.theme=e==="auto"?l():e,n(e)}matchMedia("(prefers-color-scheme: light)").addEventListener("change",()=>{c()==="auto"&&t("auto")});class s extends HTMLElement{constructor(){super(),t(c()),this.querySelector("select")?.addEventListener("change",a=>{a.currentTarget instanceof HTMLSelectElement&&t(o(a.currentTarget.value))})}}customElements.define("starlight-theme-select",s);</script> <script type="module">class s extends HTMLElement{constructor(){super();const e=this.querySelector("select");e&&(e.addEventListener("change",t=>{t.currentTarget instanceof HTMLSelectElement&&(window.location.pathname=t.currentTarget.value)}),window.addEventListener("pageshow",t=>{if(!t.persisted)return;const n=e.querySelector("option[selected]")?.index;n!==e.selectedIndex&&(e.selectedIndex=n??0)}))}}customElements.define("starlight-lang-select",s);</script> </div> </div> </header> <nav class="sidebar print:hidden astro-vrdttmbt" aria-label="Main"> <starlight-menu-button class="print:hidden astro-jif73yzw"> <button aria-expanded="false" aria-label="Menu" aria-controls="starlight__sidebar" class="sl-flex md:sl-hidden astro-jif73yzw"> <svg aria-hidden="true" class="open-menu astro-jif73yzw astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M3 8h18a1 1 0 1 0 0-2H3a1 1 0 0 0 0 2Zm18 8H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Zm0-5H3a1 1 0 0 0 0 2h18a1 1 0 0 0 0-2Z"/></svg> <svg aria-hidden="true" class="close-menu astro-jif73yzw astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="m13.41 12 6.3-6.29a1.004 1.004 0 1 0-1.42-1.42L12 10.59l-6.29-6.3a1.004 1.004 0 0 0-1.42 1.42l6.3 6.29-6.3 6.29a1 1 0 0 0 0 1.42.998.998 0 0 0 1.42 0l6.29-6.3 6.29 6.3a.999.999 0 0 0 1.42 0 1 1 0 0 0 0-1.42L13.41 12Z"/></svg> </button> </starlight-menu-button> <script type="module">class s extends HTMLElement{constructor(){super(),this.btn=this.querySelector("button"),this.btn.addEventListener("click",()=>this.toggleExpanded());const t=this.closest("nav");t&&t.addEventListener("keyup",e=>this.closeOnEscape(e))}setExpanded(t){this.setAttribute("aria-expanded",String(t)),document.body.toggleAttribute("data-mobile-menu-expanded",t)}toggleExpanded(){this.setExpanded(this.getAttribute("aria-expanded")!=="true")}closeOnEscape(t){t.code==="Escape"&&(this.setExpanded(!1),this.btn.focus())}}customElements.define("starlight-menu-button",s);</script> <div id="starlight__sidebar" class="sidebar-pane astro-vrdttmbt"> <div class="sidebar-content sl-flex astro-vrdttmbt"> <sl-sidebar-state-persist data-hash="01h4vp7" class="astro-kku4brbg"> <script aria-hidden="true">
|
||
(() => {
|
||
try {
|
||
if (!matchMedia('(min-width: 50em)').matches) return;
|
||
/** @type {HTMLElement | null} */
|
||
const target = document.querySelector('sl-sidebar-state-persist');
|
||
const state = JSON.parse(sessionStorage.getItem('sl-sidebar-state') || '0');
|
||
if (!target || !state || target.dataset.hash !== state.hash) return;
|
||
window._starlightScrollRestore = state.scroll;
|
||
customElements.define(
|
||
'sl-sidebar-restore',
|
||
class SidebarRestore extends HTMLElement {
|
||
connectedCallback() {
|
||
try {
|
||
const idx = parseInt(this.dataset.index || '');
|
||
const details = this.closest('details');
|
||
if (details && typeof state.open[idx] === 'boolean') details.open = state.open[idx];
|
||
} catch {}
|
||
}
|
||
}
|
||
);
|
||
} catch {}
|
||
})();
|
||
</script> <ul class="top-level astro-3ii7xxms"> <li class="astro-3ii7xxms"> <details open class="astro-3ii7xxms"> <sl-sidebar-restore data-index="0"></sl-sidebar-restore> <summary class="astro-3ii7xxms"> <span class="group-label astro-3ii7xxms"> <span class="large astro-3ii7xxms">ISPmail for Debian 13</span> </span> <svg aria-hidden="true" class="caret astro-3ii7xxms astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <ul class="astro-3ii7xxms"> <li class="astro-3ii7xxms"> <a href="/ispmail-trixie/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Start here</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/upgrading/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Upgrading from Debian Bookworm</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/install-debian/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Installing Debian</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/install-the-software-packages/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Install software packages</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/virtual-domains/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Virtual domains</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/database/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Database setup</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/overview/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Big picture</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/dns-records/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">DNS records</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/tls-certificate/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">TLS certificate</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/postfix/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Postfix</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/dovecot/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Dovecot</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/lmtp/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">LMTP</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/webmail/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Webmail using Roundcube</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/relaying/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Sending / Relaying</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/imap/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">IMAP</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/managing-users-aliases-and-domains/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Managing users, aliases and domains</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/catching-spam-with-rspamd/" aria-current="page" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Catching spam with rspamd</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/anti-spoofing-dkim-spf/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Prevent spoofing using DKIM and SPF</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/catch-all/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Catch-all addresses</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/quotas/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Quotas</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/going-live/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Going live</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-trixie/automated-installation/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Automated installation</span> </a> </li> </ul> </details> </li><li class="astro-3ii7xxms"> <details open class="astro-3ii7xxms"> <sl-sidebar-restore data-index="1"></sl-sidebar-restore> <summary class="astro-3ii7xxms"> <span class="group-label astro-3ii7xxms"> <span class="large astro-3ii7xxms">ISPmail for Debian 12</span> </span> <svg aria-hidden="true" class="caret astro-3ii7xxms astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <ul class="astro-3ii7xxms"> <li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Start here</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/whats-new/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">What's new</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/migrating-from-a-bullseye-to-a-bookworm-server/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Migrating from your old (Bullseye) server</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/big-picture/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">The big picture</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/types-of-email-domains/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Types of email domains</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/install-debian-bookworm-on-your-server/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Installing Debian</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/install-the-software-packages/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Install software packages</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/creating-a-tls-encryption-key-and-certificate/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">TLS certificate</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/prepare-the-database/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Preparing the database</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/making-postfix-get-its-information-from-the-mariadb-database/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Let Postfix access MariaDB</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/catchall-aliases/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Catch-all aliases</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/setting-up-dovecot/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Setting up Dovecot</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/let-postfix-send-emails-to-dovecot/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Let Postfix send emails to Dovecot</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/quotas/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Quotas</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/testing-imap/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Testing IMAP</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/webmail-using-roundcube/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Webmail using Roundcube</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/testing-email-delivery/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Testing email delivery</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/relaying-outgoing-emails-through-postfix/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Relaying outgoing emails through Postfix</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/catching-spam-with-rspamd/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Catching spam with rspamd</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/setting-dns-records/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Setting DNS records</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/prevent-spoofing-using-dkim/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Prevent spoofing using DKIM</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/managing-users-aliases-and-domains/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Managing users, aliases and domains</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/monitoring-and-backup/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Monitoring and Backup</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/automatic-installation-with-ansible/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Automatic installation with Ansible</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/success-stories/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Success stories</span> </a> </li><li class="astro-3ii7xxms"> <a href="/ispmail-bookworm/faq-frequently-asked-questions/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">FAQ (frequently asked questions)</span> </a> </li> </ul> </details> </li><li class="astro-3ii7xxms"> <details open class="astro-3ii7xxms"> <sl-sidebar-restore data-index="2"></sl-sidebar-restore> <summary class="astro-3ii7xxms"> <span class="group-label astro-3ii7xxms"> <span class="large astro-3ii7xxms">Misc articles</span> </span> <svg aria-hidden="true" class="caret astro-3ii7xxms astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.25rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg> </summary> <ul class="astro-3ii7xxms"> <li class="astro-3ii7xxms"> <a href="/article/updating-the-bios-on-lenovo-laptops-from-linux-using-a-usb-flash-stick/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Updating the BIOS on Lenovo laptops from Linux using a USB flash stick</span> </a> </li><li class="astro-3ii7xxms"> <a href="/bacula-cheatsheet/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Bareos/Bacula Cheat Sheet</span> </a> </li><li class="astro-3ii7xxms"> <a href="/debian-packages-are-so-old/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Debian packages are so old</span> </a> </li><li class="astro-3ii7xxms"> <a href="/getting-help-on-irc/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Getting help on IRC</span> </a> </li><li class="astro-3ii7xxms"> <a href="/linux/renaming-multiple-files/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Renaming multiple files</span> </a> </li><li class="astro-3ii7xxms"> <a href="/linuxtip/pipes/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Pipes and redirection</span> </a> </li><li class="astro-3ii7xxms"> <a href="/rsnapshot-and-usb-drives/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Backups with rsnaphot to external USB drives</span> </a> </li><li class="astro-3ii7xxms"> <a href="/squid-acls/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">How Squid ACLs work</span> </a> </li><li class="astro-3ii7xxms"> <a href="/understanding-lvm/" aria-current="false" class="astro-3ii7xxms"> <span class="astro-3ii7xxms">Understanding the Logical Volume Manager (LVM)</span> </a> </li> </ul> </details> </li> </ul> <script aria-hidden="true">
|
||
(() => {
|
||
const scroller = document.getElementById('starlight__sidebar');
|
||
if (!window._starlightScrollRestore || !scroller) return;
|
||
scroller.scrollTop = window._starlightScrollRestore;
|
||
delete window._starlightScrollRestore;
|
||
})();
|
||
</script> </sl-sidebar-state-persist> <div class="md:sl-hidden"> <div class="mobile-preferences sl-flex astro-wu23bvmt"> <div class="social-icons astro-wu23bvmt"> <a href="https://git.workaround.org/chaas/ispmail-workaround-org" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Git</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M0.355 12.231L0.355 12.231L0.355 11.853Q0.397 11.517 0.565 11.265L0.565 11.265Q0.691 11.097 0.943 10.803L0.943 10.803L1.279 10.425L7.831 3.831L7.873 3.873Q7.957 3.873 7.957 3.957L7.957 3.957L10.309 6.351Q10.519 6.519 10.309 6.729L10.309 6.729Q10.225 7.317 10.435 7.863Q10.645 8.409 11.107 8.703L11.107 8.703Q11.275 8.787 11.317 8.892Q11.359 8.997 11.359 9.207L11.359 9.207L11.359 15.003Q11.359 15.255 11.107 15.507L11.107 15.507Q10.603 15.843 10.372 16.389Q10.141 16.935 10.330 17.502Q10.519 18.069 10.981 18.447Q11.443 18.825 12.031 18.825Q12.619 18.825 13.081 18.489Q13.543 18.153 13.732 17.586Q13.921 17.019 13.753 16.473Q13.585 15.927 13.081 15.507L13.081 15.507Q12.913 15.423 12.871 15.318Q12.829 15.213 12.829 15.003L12.829 15.003L12.829 9.081L12.955 9.081L15.055 11.181Q15.139 11.265 15.139 11.391L15.139 11.391L15.181 11.475L15.181 12.231Q15.265 13.029 15.874 13.470Q16.483 13.911 17.281 13.806Q18.079 13.701 18.562 13.029Q19.045 12.357 18.835 11.601L18.835 11.601Q18.751 10.929 18.100 10.488Q17.449 10.047 16.735 10.131L16.735 10.131Q16.483 10.131 16.231 10.005L16.231 10.005L14.005 7.779Q13.879 7.653 13.879 7.401L13.879 7.401Q14.005 6.813 13.711 6.267Q13.417 5.721 12.871 5.448Q12.325 5.175 11.779 5.301L11.779 5.301Q11.359 5.301 11.359 5.175L11.359 5.175Q9.805 3.579 9.007 2.907L9.007 2.907Q8.797 2.739 9.007 2.529L9.007 2.529Q9.469 2.193 10.183 1.353L10.183 1.353L10.729 0.807Q11.905-0.369 13.081 0.807L13.081 0.807L23.035 10.803Q24.253 11.979 23.035 13.155L23.035 13.155L13.291 22.941Q12.913 23.319 12.745 23.445L12.745 23.445Q12.409 23.739 12.031 23.781L12.031 23.781L11.779 23.781L11.569 23.697Q11.065 23.487 10.855 23.277L10.855 23.277L10.057 22.479Q8.881 21.387 8.335 20.757L8.335 20.757L1.153 13.575Q1.069 13.365 0.775 13.029L0.775 13.029Q0.397 12.525 0.355 12.231Z"/></svg></a><a href="https://riot.im/app/#/room/#ispmail:matrix.org" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Matrix</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M22.5 1.5v21h-2.25V24H24V0h-3.75v1.5h2.25ZM7.46 7.95V9.1h.04a3.02 3.02 0 0 1 2.61-1.39c.54 0 1.03.1 1.48.32.44.2.78.58 1.01 1.1.26-.37.6-.7 1.03-.99.44-.28.95-.43 1.55-.43.45 0 .87.06 1.26.17.38.11.71.29.99.53.27.24.49.56.64.95.15.4.23.86.23 1.42v5.72h-2.34v-4.85c0-.29-.01-.56-.04-.8a1.73 1.73 0 0 0-.18-.67 1.1 1.1 0 0 0-.44-.45 1.6 1.6 0 0 0-.78-.16c-.33 0-.6.06-.8.19-.2.12-.37.29-.48.5a2 2 0 0 0-.23.69c-.04.26-.06.52-.06.78v4.77H10.6v-4.8l-.01-.75a2.29 2.29 0 0 0-.14-.69c-.08-.2-.23-.38-.42-.5a1.5 1.5 0 0 0-.85-.2c-.15.01-.3.04-.44.08-.19.06-.37.15-.52.28-.18.14-.32.34-.44.6-.12.26-.18.6-.18 1.02v4.96H5.25V7.94h2.21ZM1.5 1.5v21h2.25V24H0V0h3.75v1.5H1.5Z"/></svg></a><a href="https://comentario.workaround.org/api/rss/comments?domain=0f111a27-fbfa-48af-8beb-ab12e612d92f" rel="me" class="sl-flex astro-wy4te6ga"><span class="sr-only astro-wy4te6ga">Feed</span><svg aria-hidden="true" class="astro-wy4te6ga astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M2.88 16.88a3 3 0 0 0 0 4.24 3 3 0 0 0 4.24 0 3 3 0 0 0-4.24-4.24Zm2.83 2.83a1 1 0 0 1-1.42-1.42 1 1 0 0 1 1.42 0 1 1 0 0 1 0 1.42ZM5 12a1 1 0 0 0 0 2 5 5 0 0 1 5 5 1 1 0 0 0 2 0 7 7 0 0 0-7-7Zm0-4a1 1 0 0 0 0 2 9 9 0 0 1 9 9 1 1 0 0 0 2 0 11.08 11.08 0 0 0-3.22-7.78A11.08 11.08 0 0 0 5 8Zm10.61.39A15.11 15.11 0 0 0 5 4a1 1 0 0 0 0 2 13 13 0 0 1 13 13 1 1 0 0 0 2 0 15.11 15.11 0 0 0-4.39-10.61Z"/></svg></a> </div> <starlight-theme-select> <label style="--sl-select-width: 6.25em" class="astro-4yphtoen"> <span class="sr-only astro-4yphtoen">Select theme</span> <svg aria-hidden="true" class="icon label-icon astro-4yphtoen astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M21 14h-1V7a3 3 0 0 0-3-3H7a3 3 0 0 0-3 3v7H3a1 1 0 0 0-1 1v2a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-2a1 1 0 0 0-1-1ZM6 7a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v7H6V7Zm14 10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-1h16v1Z"/></svg> <select autocomplete="off" class="astro-4yphtoen"> <option value="dark" class="astro-4yphtoen">Dark</option><option value="light" class="astro-4yphtoen">Light</option><option value="auto" selected class="astro-4yphtoen">Auto</option> </select> <svg aria-hidden="true" class="icon caret astro-4yphtoen astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path d="M17 9.17a1 1 0 0 0-1.41 0L12 12.71 8.46 9.17a1 1 0 1 0-1.41 1.42l4.24 4.24a1.002 1.002 0 0 0 1.42 0L17 10.59a1.002 1.002 0 0 0 0-1.42Z"/></svg> </label> </starlight-theme-select> <script>
|
||
StarlightThemeProvider.updatePickers();
|
||
</script> </div> </div> </div> </div> </nav> <div class="main-frame astro-vrdttmbt"> <script type="module">const a=document.getElementById("starlight__sidebar"),n=a?.querySelector("sl-sidebar-state-persist"),o="sl-sidebar-state",i=()=>{let t=[];const e=n?.dataset.hash||"";try{const s=sessionStorage.getItem(o),r=JSON.parse(s||"{}");Array.isArray(r.open)&&r.hash===e&&(t=r.open)}catch{}return{hash:e,open:t,scroll:a?.scrollTop||0}},c=t=>{try{sessionStorage.setItem(o,JSON.stringify(t))}catch{}},d=()=>c(i()),l=(t,e)=>{const s=i();s.open[e]=t,c(s)};n?.addEventListener("click",t=>{if(!(t.target instanceof Element))return;const e=t.target.closest("summary")?.closest("details");if(!e)return;const s=e.querySelector("sl-sidebar-restore"),r=parseInt(s?.dataset.index||"");isNaN(r)||l(!e.open,r)});addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&d()});addEventListener("pageHide",d);</script> <div class="lg:sl-flex astro-67yu43on"> <aside class="right-sidebar-container print:hidden astro-67yu43on"> <div class="right-sidebar astro-67yu43on"> <div class="lg:sl-hidden astro-pb3aqygn"><mobile-starlight-toc data-min-h="2" data-max-h="3" class="astro-doynk5tl"><nav aria-labelledby="starlight__on-this-page--mobile" class="astro-doynk5tl"><details id="starlight__mobile-toc" class="astro-doynk5tl"><summary id="starlight__on-this-page--mobile" class="sl-flex astro-doynk5tl"><span class="toggle sl-flex astro-doynk5tl">On this page<svg aria-hidden="true" class="caret astro-doynk5tl astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1rem;"><path d="m14.83 11.29-4.24-4.24a1 1 0 1 0-1.42 1.41L12.71 12l-3.54 3.54a1 1 0 0 0 0 1.41 1 1 0 0 0 .71.29 1 1 0 0 0 .71-.29l4.24-4.24a1.002 1.002 0 0 0 0-1.42Z"/></svg></span><span class="display-current astro-doynk5tl"></span></summary><div class="dropdown astro-doynk5tl"><ul class="isMobile astro-g2bywc46" style="--depth: 0;"> <li class="astro-g2bywc46" style="--depth: 0;"> <a href="#_top" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Overview</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#make-postfix-use-rspamd" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Make Postfix use rspamd</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#spam-scores" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Spam scores</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#x-spam-header" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">X-Spam header</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#sending-spam-to-the-junk-folder" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Sending spam to the Junk folder</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#automatic-spam-training" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Automatic spam training</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#per-user-spam-training" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Per-user spam training</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#learning-from-user-actions" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Learning from user actions</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#logging" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Logging</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#autoexpunge" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Autoexpunge</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#rspamds-web-interface" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">rspamd’s web interface</span> </a> </li> </ul> </div></details></nav></mobile-starlight-toc><script type="module" src="/_astro/MobileTableOfContents.astro_astro_type_script_index_0_lang.C181hMzK.js"></script></div><div class="right-sidebar-panel sl-hidden lg:sl-block astro-pb3aqygn"><div class="sl-container astro-pb3aqygn"><starlight-toc data-min-h="2" data-max-h="3"><nav aria-labelledby="starlight__on-this-page"><h2 id="starlight__on-this-page">On this page</h2><ul class="astro-g2bywc46" style="--depth: 0;"> <li class="astro-g2bywc46" style="--depth: 0;"> <a href="#_top" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Overview</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#make-postfix-use-rspamd" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Make Postfix use rspamd</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#spam-scores" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Spam scores</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#x-spam-header" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">X-Spam header</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#sending-spam-to-the-junk-folder" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Sending spam to the Junk folder</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#automatic-spam-training" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Automatic spam training</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#per-user-spam-training" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Per-user spam training</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#learning-from-user-actions" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Learning from user actions</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#logging" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Logging</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#autoexpunge" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">Autoexpunge</span> </a> </li><li class="astro-g2bywc46" style="--depth: 0;"> <a href="#rspamds-web-interface" class="astro-g2bywc46" style="--depth: 0;"> <span class="astro-g2bywc46" style="--depth: 0;">rspamd’s web interface</span> </a> </li> </ul> </nav></starlight-toc><script type="module" src="/_astro/TableOfContents.astro_astro_type_script_index_0_lang.CKWWgpjV.js"></script></div></div> </div> </aside> <div class="main-pane astro-67yu43on"> <main data-pagefind-body class="astro-bguv2lll" lang="en" dir="ltr"> <div class="content-panel astro-7nkwcw3z"> <div class="sl-container astro-7nkwcw3z"> <h1 id="_top" class="astro-j6tvhyss">Catching spam with rspamd</h1> </div> </div> <div class="content-panel astro-7nkwcw3z"> <div class="sl-container astro-7nkwcw3z"> <div class="sl-markdown-content"> <aside aria-label="Optional feature" class="starlight-aside starlight-aside--tip"> <p class="starlight-aside__title" aria-hidden="true"> <svg aria-hidden="true" class="starlight-aside__icon astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1em;"><path fill-rule="evenodd" d="M1.44 8.855v-.001l3.527-3.516c.34-.344.802-.541 1.285-.548h6.649l.947-.947c3.07-3.07 6.207-3.072 7.62-2.868a1.821 1.821 0 0 1 1.557 1.557c.204 1.413.203 4.55-2.868 7.62l-.946.946v6.649a1.845 1.845 0 0 1-.549 1.286l-3.516 3.528a1.844 1.844 0 0 1-3.11-.944l-.858-4.275-4.52-4.52-2.31-.463-1.964-.394A1.847 1.847 0 0 1 .98 10.693a1.843 1.843 0 0 1 .46-1.838Zm5.379 2.017-3.873-.776L6.32 6.733h4.638l-4.14 4.14Zm8.403-5.655c2.459-2.46 4.856-2.463 5.89-2.33.134 1.035.13 3.432-2.329 5.891l-6.71 6.71-3.561-3.56 6.71-6.711Zm-1.318 15.837-.776-3.873 4.14-4.14v4.639l-3.364 3.374Z" clip-rule="evenodd"/><path d="M9.318 18.345a.972.972 0 0 0-1.86-.561c-.482 1.435-1.687 2.204-2.934 2.619a8.22 8.22 0 0 1-1.23.302c.062-.365.157-.79.303-1.229.415-1.247 1.184-2.452 2.62-2.935a.971.971 0 1 0-.62-1.842c-.12.04-.236.084-.35.13-2.02.828-3.012 2.588-3.493 4.033a10.383 10.383 0 0 0-.51 2.845l-.001.016v.063c0 .536.434.972.97.972H2.24a7.21 7.21 0 0 0 .878-.065c.527-.063 1.248-.19 2.02-.447 1.445-.48 3.205-1.472 4.033-3.494a5.828 5.828 0 0 0 .147-.407Z"/></svg>Optional feature </p> <div class="starlight-aside__content"> <p>This feature is completely optional. Just skip this page if you don’t care about filtering out spam emails.</p> </div> </aside>
|
||
<p>You have come a long way in this guide and your mail server is already fully functional. Now it’s time to deal with
|
||
filtering out incoming spam emails. I found that <a href="https://rspamd.com/">rspamd</a> is well-performing choice for that
|
||
purpose both in speed and detection. rspamd keeps a permanent process running on your mail server that listens to
|
||
connections from Postfix using the <a href="http://www.postfix.org/MILTER_README.html">milter</a> (=<strong>m</strong>ail f<strong>ilter</strong>) protocol.
|
||
Every time an email enters your system, Postfix will send it to rspamd to have its content checked. rspamd runs a lot of
|
||
checks on the email and computes a total score. The higher the score – the more likely it it spam. And the best thing
|
||
about milters: the check happens in parallel while Postfix is still in the process of receiving the email. If the score
|
||
is high enough, Postfix can still reject the email right at the doorstep.</p>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="make-postfix-use-rspamd">Make Postfix use rspamd</h2><a class="sl-anchor-link" href="#make-postfix-use-rspamd"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Make Postfix use rspamd”</span></a></div>
|
||
<p>Let’s tell Postfix to send all incoming email through rspamd:</p>
|
||
<div class="expressive-code"><link rel="stylesheet" href="/_astro/ec.v4551.css"><script type="module" src="/_astro/ec.0vx5m.js"></script><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">postconf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">smtpd_milters=inet:127.0.0.1:11332</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">postconf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">non_smtpd_milters=inet:127.0.0.1:11332</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="postconf smtpd_milters=inet:127.0.0.1:11332postconf non_smtpd_milters=inet:127.0.0.1:11332"><div></div></button></div></figure></div>
|
||
<p>For testing we can use a sample spam email that comes with SpamAssassin. It is called <strong>GTUBE</strong> (<strong>G</strong>eneric <strong>T</strong>est
|
||
for <strong>U</strong>nsolicited <strong>B</strong>ulk <strong>E</strong>mail). It contains a certain artificial pattern that is recognized as spam by
|
||
SpamAssassin. Do you know EICAR.COM to test virus scanners? This is the same thing for spam.</p>
|
||
<p>Get the GTUBE file, send it to John and let’s see what happens:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">wget</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">http://spamassassin.apache.org/gtube/gtube.txt</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sendmail</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">john@example.org</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">gtube.txt</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">journalctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-fu</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">postfix</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="wget http://spamassassin.apache.org/gtube/gtube.txtsendmail john@example.org < gtube.txtjournalctl -fu postfix"><div></div></button></div></figure></div>
|
||
<p>You will find something like this at the end of the log file:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">postfix/cleanup[1386519]: 2B72320126: message-id=<GTUBE1.1010101@example.net></span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">postfix/cleanup[1386519]: 2B72320126: milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 5.7.1 Gtube pattern; from=<root@mailserver> to=<john@example.org></span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">postfix/cleanup[1386519]: 2B72320126: to=<john@example.org>, relay=none, delay=0.16, delays=0.16/0/0/0, dsn=5.7.1, status=bounced (Gtube pattern)</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="postfix/cleanup[1386519]: 2B72320126: message-id=<GTUBE1.1010101@example.net>postfix/cleanup[1386519]: 2B72320126: milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 5.7.1 Gtube pattern; from=<root@mailserver> to=<john@example.org>postfix/cleanup[1386519]: 2B72320126: to=<john@example.org>, relay=none, delay=0.16, delays=0.16/0/0/0, dsn=5.7.1, status=bounced (Gtube pattern)"><div></div></button></div></figure></div>
|
||
<p><strong>milter-reject</strong> tells that the rspamd recommended to reject the email. It gave the reason <code dir="auto">5.7.1 Gtube pattern</code>. SMTP
|
||
and LMTP always uses such three digit codes. They are defined in <a href="https://tools.ietf.org/html/rfc3463">RFC 3463</a>. The
|
||
first digit is most important:</p>
|
||
<ul>
|
||
<li>2 = Success</li>
|
||
<li>4 = Temporary failure (come back later)</li>
|
||
<li>5 = Permanent failure (do not try again in this way)</li>
|
||
</ul>
|
||
<p>So <code dir="auto">5.7.1</code> tells us that the result code is a permanent failure in delivery. If you looked up the RFC you would find
|
||
that the 7 stands for an issue regarding the security policy. So it’s not a technical failure but instead a
|
||
security-relevant component on the system has rejected the email. That’s what rspamd did. It even told us the reason:
|
||
<code dir="auto">Gtube pattern</code>. So you see that rspamd knows about the Gtube spam test pattern and reacts as expected.</p>
|
||
<p>That email will not be delivered to John. Instead Postfix will reject it at the doorstep – while it is still in the SMTP
|
||
communication with the sending server. It just hung up the phone and now it’s the sending server’s problem to deal with.</p>
|
||
<p>It is important to either reject an email right away or to deliver it to a user. Never accept an email and later decide
|
||
to complain about it to the alleged sender. Trust me: the sender you see on a spam email is never the bad guy who sent
|
||
it. You would complain to the wrong person which is called
|
||
<a href="https://en.wikipedia.org/wiki/Backscatter_(email)">backscatter</a>.</p>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="spam-scores">Spam scores</h2><a class="sl-anchor-link" href="#spam-scores"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Spam scores”</span></a></div>
|
||
<p>rspamd will however not reject all spam email. It computes a score that tells how likely a certain email is spam. You
|
||
can tell it which scores you would accept, flag as spam or make the incoming email get rejected. Rspamd checks incoming
|
||
emails in various ways. Take a look at the <code dir="auto">/etc/rspamd/actions.conf</code> file:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">actions {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">reject = 15;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">add_header = 6;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">greylist = 4;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="actions { reject = 15; add_header = 6; greylist = 4;}"><div></div></button></div></figure></div>
|
||
<p>These are the default actions. If rspamd computes a score of at least 15 then the email will get rejected right away
|
||
just like the <em>Gtube pattern</em> in the previous test. Any other score above 6 will add a line “X-Spam: Yes” so that your
|
||
mail software can detect them and maybe file the email to a different folder. And any other score above 4 will trigger
|
||
<a href="https://en.wikipedia.org/wiki/Greylisting_(email)">greylisting</a> which is a mechanism that temporarily rejects the
|
||
email with a 4.x.x code and waits if the sending server will try again. After a waiting time of a few minutes
|
||
greylisting will accept the email. The idea is to reject email from systems that do not have a sending queue. Malware
|
||
like on infected Wind*ws computers used to try sending an email just once which triggered greylisting and successfully
|
||
rejected the spammer. But even malware programmers have learned and may try again after a few minutes thus circumventing
|
||
greylisting. Your mileage may vary. The problem with greylisting is that the recipient has to wait a couple of minutes
|
||
for the email to be delivered which may be bothering the users.</p>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="x-spam-header">X-Spam header</h2><a class="sl-anchor-link" href="#x-spam-header"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “X-Spam header”</span></a></div>
|
||
<p>An email consists of <strong>headers</strong> and the actual <strong>body</strong>. Your users will usually only see common header information
|
||
like the <em>subject</em>, the <em>sender</em>, the <em>recipient</em> and the <em>time</em> the email was sent. But there is way more information
|
||
like the route the email travelled. Mail software can even add arbitrary <strong>extended headers</strong> that start with <code dir="auto">X-</code>.
|
||
There is one specific header that rspamd adds for us if it finds a spam email:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">X-Spam: Yes</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="X-Spam: Yes"><div></div></button></div></figure></div>
|
||
<details class="collapsible"><summary>Click here to dive deeper into rspamd’s scores…</summary><p>rspamd can add an <code dir="auto">X-Spamd-Result</code> header containing the various criteria that added to the total score:</p><div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">X-Rspamd-Server: mailserver</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">X-Rspamd-Queue-Id: C22E55A005B3</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">X-Spamd-Result: default: False [11.55 / 15.00]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">R_PARTS_DIFFER(0.27)[63.4%]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">FROM_NO_DN(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">RCVD_COUNT_ZERO(0.00)[0]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">R_DKIM_NA(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">FUZZY_DENIED(12.00)[1:19305c7fdd:1.00:bin,1:35699594fd:0.91:txt]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">RBL_SENDERSCORE(2.00)[55.181.23.94.bl.score.senderscore.com]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">ARC_NA(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">RCPT_COUNT_ONE(0.00)[1]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">RCVD_TLS_ALL(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">FROM_EQ_ENVFROM(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">R_SPF_SOFTFAIL(0.00)[~all]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">BAYES_HAM(-2.71)[98.75%]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">TO_MATCH_ENVRCPT_ALL(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">MIME_GOOD(-0.10)[multipart/related,multipart/alternative,text/plain]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">MID_RHS_MATCH_FROM(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">ASN(0.00)[asn:16276, ipnet:94.23.0.0/16, country:FR]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">TO_DN_NONE(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">DMARC_POLICY_SOFTFAIL(0.10)[Chronopost.fr : No valid SPF, No valid DKIM,none]</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">SUBJECT_ENDS_EXCLAIM(0.00)[]</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">X-Spam: Yes</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="X-Rspamd-Server: mailserverX-Rspamd-Queue-Id: C22E55A005B3X-Spamd-Result: default: False [11.55 / 15.00] R_PARTS_DIFFER(0.27)[63.4%] FROM_NO_DN(0.00)[] RCVD_COUNT_ZERO(0.00)[0] R_DKIM_NA(0.00)[] FUZZY_DENIED(12.00)[1:19305c7fdd:1.00:bin,1:35699594fd:0.91:txt] RBL_SENDERSCORE(2.00)[55.181.23.94.bl.score.senderscore.com] ARC_NA(0.00)[] RCPT_COUNT_ONE(0.00)[1] RCVD_TLS_ALL(0.00)[] FROM_EQ_ENVFROM(0.00)[] R_SPF_SOFTFAIL(0.00)[~all] BAYES_HAM(-2.71)[98.75%] TO_MATCH_ENVRCPT_ALL(0.00)[] MIME_GOOD(-0.10)[multipart/related,multipart/alternative,text/plain] MID_RHS_MATCH_FROM(0.00)[] ASN(0.00)[asn:16276, ipnet:94.23.0.0/16, country:FR] TO_DN_NONE(0.00)[] DMARC_POLICY_SOFTFAIL(0.10)[Chronopost.fr : No valid SPF, No valid DKIM,none] SUBJECT_ENDS_EXCLAIM(0.00)[]X-Spam: Yes"><div></div></button></div></figure></div><p>Enable those additonal headers:</p><div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Enable rspamd's extended headers</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/rspamd/local.d/milter_headers.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#3B61B0">extended_spam_headers = true;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart rspamd</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">rspamd</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Send a normal (non-spam) test email. Do not use "localhost" here because rspamd</span></div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># will only add extended headers for emails coming from the internet.</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">swaks</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--server</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">mailserver.example.org</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--to</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">john@example.org</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Enable rspamd's extended headerscat > /etc/rspamd/local.d/milter_headers.conf << EOFextended_spam_headers = true;EOF# Restart rspamdsystemctl reload rspamd# Send a normal (non-spam) test email. Do not use "localhost" here because rspamd# will only add extended headers for emails coming from the internet.swaks --server mailserver.example.org --to john@example.org"><div></div></button></div></figure></div><p>Each of the uppercase symbols like <em>FROM_HAS_DN</em> means that a certain detection rule of rspamd was triggered. It does
|
||
not necessarily mean something bad about the email. For example <em>R_SPF_ALLOW</em> has a negative score that lowers the total
|
||
score because it is something good about the email. There are a several symbols with a 0.00 score. These do not change
|
||
the score but show you what rspamd has found. But if you consider certain criteria good or bad then you can
|
||
<a href="https://docs.rspamd.com/configuration/metrics/#configuring-scores-and-actions">define your own scores</a> for them.</p></details>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="sending-spam-to-the-junk-folder">Sending spam to the Junk folder</h2><a class="sl-anchor-link" href="#sending-spam-to-the-junk-folder"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Sending spam to the Junk folder”</span></a></div>
|
||
<p>Your users will not realize that their spam emails have an added <code dir="auto">X-Spam: Yes</code> header. It is not shown in their mail
|
||
client. Nor does it move the email out of the inbox into their spam folder. Such emails just appear in their inbox. So
|
||
let’s be nice and move spam emails to the user’s <em>Junk</em> folder automatically. Dovecot has support
|
||
for <a href="https://en.wikipedia.org/wiki/Sieve_(mail_filtering_language)">Sieve</a> filtering rules that are if-then scripts
|
||
that get run whenever an email arrives.</p>
|
||
<p>John could create such a Sieve script for himself (using the Roundcube webmail interface). But let’s find a solution
|
||
that applies to all your users. Create a new config file (<code dir="auto">/etc/dovecot/conf.d/99-ispmail-sieve.conf</code>) that tells
|
||
Dovecot:</p>
|
||
<ul>
|
||
<li>whenever an email is delivered to our users, run an additional Sieve script
|
||
(<code dir="auto">/etc/dovecot/sieve/spam-to-junk-folder.sieve</code>)</li>
|
||
<li>the Sieve functionality is enabled during LMTP (when an email is passed on from Postfix to Dovecot)</li>
|
||
<li>the user gets a <em>Junk</em> folder created in his mailbox and is subscribed to it so that it appears in their mail program</li>
|
||
</ul>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create a new config file for spam handling</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/conf.d/99-ispmail-sieve-movetojunk.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">sieve_script spam-to-junk-folder {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">driver = file</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">type = after</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">path = /etc/dovecot/sieve/spam-to-junk-folder.sieve</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Enable the execution of Sieve rules when Postfix sends an email to Dovecot over LMTP</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">protocol lmtp {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">mail_plugins {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">sieve = yes</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Make sure that every user has a Junk folder and is subscribed to it</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">namespace inbox {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">mailbox Junk {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">special_use = \Junk</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">auto = subscribe</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart Dovecot</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Create a new config file for spam handlingcat > /etc/dovecot/conf.d/99-ispmail-sieve-movetojunk.conf << 'EOF'sieve_script spam-to-junk-folder { driver = file type = after path = /etc/dovecot/sieve/spam-to-junk-folder.sieve}# Enable the execution of Sieve rules when Postfix sends an email to Dovecot over LMTPprotocol lmtp { mail_plugins { sieve = yes }}# Make sure that every user has a Junk folder and is subscribed to itnamespace inbox { mailbox Junk { special_use = \Junk auto = subscribe }}EOF# Restart Dovecotsystemctl reload dovecot"><div></div></button></div></figure></div>
|
||
<p>Now we need to create that Sieve script (<code dir="auto">/etc/dovecot/sieve/spam-to-junk-folder.sieve</code>) that is run on each delivery.
|
||
Its job is to check if the <code dir="auto">X-Spam: yes</code> header is present. If it is, the email is filed into the user’s <code dir="auto">Junk</code> folder.</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create the directory for Sieve files</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">mkdir</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-p</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create the Sieve script to move Spam mails to the user's Junk folder</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/spam-to-junk-folder.sieve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">require ["fileinto"];</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">if header :contains "X-Spam" "Yes" {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">fileinto "Junk";</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">stop;</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Make the sieve script machine-readable</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sievec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/spam-to-junk-folder.sieve</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Create the directory for Sieve filesmkdir -p /etc/dovecot/sieve# Create the Sieve script to move Spam mails to the user's Junk foldercat > /etc/dovecot/sieve/spam-to-junk-folder.sieve << 'EOF'require ["fileinto"];if header :contains "X-Spam" "Yes" { fileinto "Junk"; stop;}EOF# Make the sieve script machine-readablesievec /etc/dovecot/sieve/spam-to-junk-folder.sieve"><div></div></button></div></figure></div>
|
||
<p>Let’s give it a test using Swaks. This time we impersonate Postfix and inject an email with an <code dir="auto">X-Spam: yes</code> header
|
||
directly into Dovecot using the LMTP socket:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">swaks</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--to</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">john@example.org</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--header-X-Spam</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">yes</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--socket</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/var/spool/postfix/private/dovecot-lmtp</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">--protocol</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">LMTP</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">journalctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-fu</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="swaks --to john@example.org --header-X-Spam "yes" --socket /var/spool/postfix/private/dovecot-lmtp --protocol LMTPjournalctl -fu dovecot"><div></div></button></div></figure></div>
|
||
<p>It should read:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">dovecot[1434406]: lmtp(john@example.org)<1436598><kh4lDNl26Wi26xUA5ANL0g>: sieve: msgid=<20251010211257.1436597@mailserver>: fileinto action: stored mail into mailbox 'Junk'</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="dovecot[1434406]: lmtp(john@example.org)<1436598><kh4lDNl26Wi26xUA5ANL0g>: sieve: msgid=<20251010211257.1436597@mailserver>: fileinto action: stored mail into mailbox 'Junk'"><div></div></button></div></figure></div>
|
||
<p>The alleged spam email has been moved to the <em>Junk</em> folder just like we wanted.</p>
|
||
<details class="collapsible"><summary>Click here to get more detailed logs…</summary><p>If you find that the delivery to the <em>Junk</em> folder did not work, you may want to increase the log level. Uncomment the
|
||
line</p><div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">log_debug=category=sieve</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="log_debug=category=sieve"><div></div></button></div></figure></div><p>in the <code dir="auto">99-ispmail-sieve.conf</code> file.</p><p>That will give you a much deeper insight of what Dovecot has been doing. Restart Dovecot, send another email with
|
||
<em>swaks</em> and check the logs again:</p><div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">journalctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-eu</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="systemctl reload dovecotjournalctl -eu dovecot"><div></div></button></div></figure></div></details>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="automatic-spam-training">Automatic spam training</h2><a class="sl-anchor-link" href="#automatic-spam-training"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Automatic spam training”</span></a></div>
|
||
<p>One of rspamd’s features is analyzing word patterns using probability theory. That functionality is contained in its
|
||
<a href="https://rspamd.com/doc/configuration/statistic.html">statistical module</a>. (Yes, the name is misleading.) Essentially
|
||
you show rspamd lots of <strong>ham</strong> (good) and <strong>spam</strong> (bad) emails and its detection gets better over time.</p>
|
||
<p>Rspamd stores that training data (among other information) in a local <a href="https://redis.io/">Redis</a> database.</p>
|
||
<details class="collapsible"><summary>Click here to learn more about Redis…</summary><p>Redis is a kind of database system. It is way more limited than a traditional SQL database because it just stores keys
|
||
and values. There aren’t several fields/columns like in SQL. But it is lightning fast the way it works. On my aged
|
||
server it handles around 50,000 requests per second. It gets it speed from its simplicity and from keeping the data in
|
||
RAM. So it doesn’t access the disk to fetch information. (But it copies its data to disk frequently to prevent data
|
||
loss.) People use Redis as a cache or for very fast lookups of simple data structures. And so
|
||
<a href="https://docs.rspamd.com/configuration/redis/">does rspamd</a>.</p></details>
|
||
<p>You have already installed the “redis-server” package earlier. It has started automatically and listens to incoming
|
||
connections on TCP port 6379 on localhost. You just need to tell rspamd to use it:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create a config file to enable automatic spam training</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/rspamd/local.d/classifier-bayes.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Store training data in the Redis database</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">servers = "127.0.0.1:6379";</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">backend = "redis";</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Enable automatic training</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">autolearn = true; # if rspamd is sure that an email is spam, it will be learned</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">min_learns = 200; # do not trust the data before at least 200 mails have been learned</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart rspamd</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">restart</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">rspamd</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Create a config file to enable automatic spam trainingcat > /etc/rspamd/local.d/classifier-bayes.conf << 'EOF'# Store training data in the Redis databaseservers = "127.0.0.1:6379";backend = "redis";# Enable automatic trainingautolearn = true; # if rspamd is sure that an email is spam, it will be learnedmin_learns = 200; # do not trust the data before at least 200 mails have been learnedEOF# Restart rspamdsystemctl restart rspamd"><div></div></button></div></figure></div>
|
||
<p>You will start with an empty training database. But that is not as bad as it sounds, because rspamd checks many
|
||
properties of an email to determine if an email is ham or spam. If there is enough evidence that an email is likely ham
|
||
or spam, then autolearning adds it to its training database.
|
||
The <a href="https://rspamd.com/doc/configuration/statistic.html">rspamd documentation</a> has further examples how to fine-tune
|
||
auto learning. After a few hundred emails the training will contribute towards a better detection rate.</p>
|
||
<p>The defaults for auto-learning are:</p>
|
||
<ul>
|
||
<li>score < -0.5 ➞ learn as ham/good</li>
|
||
<li>score > 4.0 ➞ learn as spam/bad</li>
|
||
</ul>
|
||
<p>See <a href="https://docs.rspamd.com/configuration/statistic/#statistics-configuration">rspamd’s documentation</a> if you want to
|
||
fine-tune that.</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">rspamc</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">stat</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="rspamc stat"><div></div></button></div></figure></div>
|
||
<p>Bayes spam checking will not work before it learned at least 200 spam and ham emails. Teaching rspamd fewer emails or
|
||
just spam emails will not work. This is defined by the <em>min_learns</em> variable defined in /etc/rspamd/statistic.conf.</p>
|
||
<p>In the output you will find lines beginning with “Statfile” like these…</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">Statfile: BAYES_SPAM type: redis; length: 0; free blocks: 0;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">total blocks: 0; free: 0.00%; learned: 21164;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">users: 214; languages: 0</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">Statfile: BAYES_HAM type: redis; length: 0; free blocks: 0;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">total blocks: 0; free: 0.00%; learned: 1411;</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">users: 62; languages: 0</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="Statfile: BAYES_SPAM type: redis; length: 0; free blocks: 0; total blocks: 0; free: 0.00%; learned: 21164; users: 214; languages: 0Statfile: BAYES_HAM type: redis; length: 0; free blocks: 0; total blocks: 0; free: 0.00%; learned: 1411; users: 62; languages: 0"><div></div></button></div></figure></div>
|
||
<p>(Don’t worry about the <code dir="auto">length: 0</code>. That seems to be a <a href="https://github.com/rspamd/rspamd/issues/3105">bug</a> that has been
|
||
ignored since 2019. Checking the actual contents of the Redis database reveals that there is actually data stored.)</p>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="per-user-spam-training">Per-user spam training</h2><a class="sl-anchor-link" href="#per-user-spam-training"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Per-user spam training”</span></a></div>
|
||
<p>Usually the training database applies to all incoming emails for <strong>all</strong> users. But you split it up so that each
|
||
recipient gets their own training.</p>
|
||
<p><strong>Advantage:</strong> users work differently. Some have subscribed to a sales newsletter and now believe that marking it as
|
||
spam gets them unsubscribed. Yes, that’s stupid but can thoroughly confuse the spam detection. Also you might not be
|
||
very interested in “blue pills” while others are. So the training data of one user does not affect that of the others.</p>
|
||
<p><strong>Disadvantage:</strong> training still requires many ham and spam mails before it has any effect. So unless a user gets 200
|
||
samples of good and bad emails, spam detection cannot work. Many users will not get that many emails so due to the lack
|
||
of spam training the detection will not be improved. For a friends-and-family server I would suggest not to use it.</p>
|
||
<p>If you decide you want to use per-user spam training then add/edit the file <code dir="auto">/etc/rspamd/local.d/classifier-bayes.conf</code>
|
||
and insert:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">users_enabled = true;</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="users_enabled = true;"><div></div></button></div></figure></div>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="learning-from-user-actions">Learning from user actions</h2><a class="sl-anchor-link" href="#learning-from-user-actions"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Learning from user actions”</span></a></div>
|
||
<p>Now we are getting to something really cool. Imagine you receive a spam email in your inbox that Rspamd didn’t catch.
|
||
You could just move it to your Junk folder — but that wouldn’t help Rspamd learn to recognize that kind of spam in the
|
||
future. Luckily, we can change that.</p>
|
||
<p>Let’s tell Dovecot that moving emails <strong>into</strong> the Junk folder teaches rspamd instantly that the email is <strong>spam</strong>. And
|
||
if the email is moved <strong>out of</strong> the Junk folder, then learn it as <strong>ham</strong>. That can be done using another <em>sieve</em>
|
||
script. Sieve scripts not only apply when an email is delivered. Thanks to Dovecot’s
|
||
<a href="https://doc.dovecot.org/2.4.1/core/plugins/imap_sieve.html#imapsieve-plugin-imap-sieve/">IMAPSieve</a> plugin, such
|
||
scripts can also be triggered if a user moves a mail between folders.</p>
|
||
<p>The setup requires three parts:</p>
|
||
<ol>
|
||
<li>Tap into the IMAP connection to know when an email is moved out of or into the Junk folder.</li>
|
||
<li>Create Sieve scripts that call Shell scripts.</li>
|
||
<li>Create Shell scripts that actually call <code dir="auto">rspamd learn_ham</code> or <code dir="auto">rspamd learn_spam</code>.</li>
|
||
</ol>
|
||
<p>Or as a diagram:</p>
|
||
<img alt="Diagram showing how to learn ham/spam when moving mails" src="/imapsieve/imapsieve-learn-spam.svg" width="100%"/>
|
||
<p>Create a new Dovecot configuration file (<code dir="auto">99-ispmail-imapsieve.conf</code>) to enable the required functionality and add
|
||
triggers on the Junk folder:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/conf.d/99-ispmail-imapsieve.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0"><</span><span style="--0:#7FDBCA;--1:#096E72"><</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'</span><span style="--0:#ECC48D;--1:#984E4D">EOF</span><span style="--0:#D9F5DD;--1:#111111">'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Enable the imap_sieve plugin</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">protocol</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">imap</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">mail_plugins</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">imap_sieve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">yes</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Allow the use of the pipe plugin to send mails to shell scripts</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sieve_plugins</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">sieve_extprograms</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">yes</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">sieve_imapsieve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">yes</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sieve_global_extensions</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">vnd.dovecot.pipe</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">yes</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Where to look for Sieve scripts that use the Pipe functionality</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sieve_pipe_bin_dir</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Moved into Junk? -> Learn as spam.</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">mailbox</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">Junk</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">sieve_script</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">spam</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#C5E478;--1:#3B61B0">type</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">before</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">cause</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">copy</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">path</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-spam.sieve</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Moved out of Junk? -> Learn as ham.</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">imapsieve_from</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">Junk</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">sieve_script</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">ham</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">{</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#C5E478;--1:#3B61B0">type</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">before</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">cause</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">copy</span></div></div><div class="ec-line"><div class="code"><span class="indent"> </span><span style="--0:#82AAFF;--1:#3B61B0">path</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-ham.sieve</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#D6DEEB;--1:#403F53"> </span></span><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D6DEEB;--1:#403F53">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="cat > /etc/dovecot/conf.d/99-ispmail-imapsieve.conf << 'EOF'# Enable the imap_sieve pluginprotocol imap { mail_plugins { imap_sieve = yes }}# Allow the use of the pipe plugin to send mails to shell scriptssieve_plugins { sieve_extprograms = yes sieve_imapsieve = yes}sieve_global_extensions { vnd.dovecot.pipe = yes}# Where to look for Sieve scripts that use the Pipe functionalitysieve_pipe_bin_dir = /etc/dovecot/sieve# Moved into Junk? -> Learn as spam.mailbox Junk { sieve_script spam { type = before cause = copy path = /etc/dovecot/sieve/learn-spam.sieve }}# Moved out of Junk? -> Learn as ham.imapsieve_from Junk { sieve_script ham { type = before cause = copy path = /etc/dovecot/sieve/learn-ham.sieve }}EOFsystemctl reload dovecot"><div></div></button></div></figure></div>
|
||
<p>The first rule tells Dovecot to run a Sieve script at <code dir="auto">/etc/dovecot/sieve/learn-spam.sieve</code> whenever an email is moved
|
||
<strong>into</strong> a user’s “Junk” folder. We will create that Sieve script in a minute.</p>
|
||
<p>The second rule works the other way. Whenever an email is moved <strong>out</strong> of the “Junk” folder to any other folder, then
|
||
the <code dir="auto">/etc/dovecot/sieve/learn-ham.sieve</code> Sieve script is called.</p>
|
||
<p>Let’s create both Sieve scripts:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create spam learning script</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-spam.sieve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">require ["vnd.dovecot.pipe", "copy", "imapsieve"];</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">pipe :copy "rspamd-learn-spam.sh";</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create ham learning script</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-ham.sieve</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">require ["vnd.dovecot.pipe", "copy", "imapsieve", "variables"];</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">pipe :copy "rspamd-learn-ham.sh";</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Compile the Sieve scripts</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sievec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-spam.sieve</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">sievec</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-ham.sieve</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Fix permissions</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">chmod</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">u=rw,go=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-{spam,ham}.{sieve,svbin}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">chown</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">vmail:vmail</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/learn-{spam,ham}.{sieve,svbin}</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Create spam learning scriptcat > /etc/dovecot/sieve/learn-spam.sieve << 'EOF'require ["vnd.dovecot.pipe", "copy", "imapsieve"];pipe :copy "rspamd-learn-spam.sh";EOF# Create ham learning scriptcat > /etc/dovecot/sieve/learn-ham.sieve << 'EOF'require ["vnd.dovecot.pipe", "copy", "imapsieve", "variables"];pipe :copy "rspamd-learn-ham.sh";EOF# Compile the Sieve scriptssievec /etc/dovecot/sieve/learn-spam.sievesievec /etc/dovecot/sieve/learn-ham.sieve# Fix permissionschmod u=rw,go= /etc/dovecot/sieve/learn-{spam,ham}.{sieve,svbin}chown vmail:vmail /etc/dovecot/sieve/learn-{spam,ham}.{sieve,svbin}"><div></div></button></div></figure></div>
|
||
<p>By the way: the <code dir="auto">sievec</code> comment compiles the <code dir="auto">*.sieve</code> file into <code dir="auto">*.svbin</code> files that Dovecot can work with. If you are
|
||
not doing this step, Dovecot will do that during runtime. However then you need to make sure that Dovecot has proper
|
||
write permissions to the target directory. The <code dir="auto">sievec</code> command is also useful because it scans a <code dir="auto">*.sieve</code> file for
|
||
errors. So if your changes do not seem to work it might be worth compiling them and looking for errors.</p>
|
||
<p>And the last step is to create the simple shell scripts that do the actual spam/ham training:</p>
|
||
<div class="expressive-code"><figure class="frame has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create the shell script for learning spam</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/rspamd-learn-spam.sh</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">#!/bin/sh</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Receives an email from Dovecot's Sieve script and pipe it into rspamc</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">exec /usr/bin/rspamc learn_spam</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create the shell script for learning ham</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/rspamd-learn-ham.sh</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">#!/bin/sh</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Receives an email from Dovecot's Sieve script and pipe it into rspamc</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">exec /usr/bin/rspamc learn_ham</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Fix permissions of the shell scripts</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">chmod</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">u=rwx,go=</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/rspamd-learn-{spam,ham}.sh</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">chown</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">vmail:vmail</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/sieve/rspamd-learn-{spam,ham}.sh</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Create the shell script for learning spamcat > /etc/dovecot/sieve/rspamd-learn-spam.sh << 'EOF'#!/bin/sh# Receives an email from Dovecot's Sieve script and pipe it into rspamcexec /usr/bin/rspamc learn_spamEOF# Create the shell script for learning hamcat > /etc/dovecot/sieve/rspamd-learn-ham.sh << 'EOF'#!/bin/sh# Receives an email from Dovecot's Sieve script and pipe it into rspamcexec /usr/bin/rspamc learn_hamEOF# Fix permissions of the shell scriptschmod u=rwx,go= /etc/dovecot/sieve/rspamd-learn-{spam,ham}.shchown vmail:vmail /etc/dovecot/sieve/rspamd-learn-{spam,ham}.sh"><div></div></button></div></figure></div>
|
||
<p>I am sure you are eager to try it out. Of course we don’t blindly believe that it works. We want to see it do its job.
|
||
Usually you will not see much unless you increase the log level to <em>debug</em>. Let’s do that:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal not-content"><figcaption class="header"><span class="title"></span><span class="sr-only">Terminal window</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/conf.d/99-ispmail-sieve-debug.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Enable detailed logging of Sieve scripts</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">log_debug=category=sieve</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart Dovecot</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Follow Dovecot's logs</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">journalctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-fu</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="cat > /etc/dovecot/conf.d/99-ispmail-sieve-debug.conf << 'EOF'# Enable detailed logging of Sieve scriptslog_debug=category=sieveEOF# Restart Dovecotsystemctl reload dovecot# Follow Dovecot's logsjournalctl -fu dovecot"><div></div></button></div></figure></div>
|
||
<p>Now open your IMAP client (Thunderbird, Evolution, Roundcube, mutt or whatever you prefer) and drag an email to John’s
|
||
Junk folder. The mail log will show a lot of things that are going on. You will see a whole screen full of things that
|
||
are going on. Some relevant lines:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: Mailbox Junk: imapsieve: storage spam: file: Using Sieve script path: /etc/dovecot/sieve/learn-spam.sieve</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: Mailbox Junk: imapsieve: storage spam: file: script 'learn-spam': Opened from 'spam'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: Opening script 1 of 1 from 'spam/learn-spam'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: Executing script from '/etc/dovecot/sieve/learn-spam.svbin'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: action pipe: running program: rspamd-learn-spam.sh</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: uid=19: Finalize pipe action (status=ok, action_status=ok, commit_status=ok, pre-commit=yes)</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: execute exec:/etc/dovecot/sieve/rspamd-learn-spam.sh (1737484): Connected to program</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: uid=19: pipe action: piped message to program 'rspamd-learn-spam.sh'</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: Mailbox Junk: imapsieve: storage spam: file: Using Sieve script path: /etc/dovecot/sieve/learn-spam.sieveimap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: Mailbox Junk: imapsieve: storage spam: file: script 'learn-spam': Opened from 'spam'imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: Opening script 1 of 1 from 'spam/learn-spam'imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: Executing script from '/etc/dovecot/sieve/learn-spam.svbin'imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: action pipe: running program: rspamd-learn-spam.shimap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: uid=19: Finalize pipe action (status=ok, action_status=ok, commit_status=ok, pre-commit=yes)imap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: execute exec:/etc/dovecot/sieve/rspamd-learn-spam.sh (1737484): Connected to programimap(john@example.org)<1737478><FnGIR2BBA/5bar5M>: Debug: sieve: uid=19: pipe action: piped message to program 'rspamd-learn-spam.sh'"><div></div></button></div></figure></div>
|
||
<p>Something similar should happen when you drag the email back from the Junk folder to the Inbox.</p>
|
||
<p>The <code dir="auto">/var/log/rspamd/rspamd.log</code> file will also show that training:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">rspamd_controller_learn_fin_task: <127.0.0.1> learned message as spam: GTUBE1.1010101@example.net</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">rspamd_controller_learn_fin_task: <127.0.0.1> learned message as ham: 20251101173211.068172@mailserver</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="rspamd_controller_learn_fin_task: <127.0.0.1> learned message as spam: GTUBE1.1010101@example.netrspamd_controller_learn_fin_task: <127.0.0.1> learned message as ham: 20251101173211.068172@mailserver"><div></div></button></div></figure></div>
|
||
<p>If you are happy and see no errors (they are highlighted in red), then switch off the debugging again.</p>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="logging">Logging</h2><a class="sl-anchor-link" href="#logging"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Logging”</span></a></div>
|
||
<p>rspamd keeps a verbose log of its actions in /var/log/rspamd/rspamd.log. If a user complains that a certain email got
|
||
blocked or at least flagged as spam then take a look at this log. You can match the /var/log/mail.log with it by
|
||
comparing the Postfix queue ID. Those are the 12-digit hexadecimal number like “<strong>95CE05A00547</strong>”. Those IDs can be
|
||
found in the rspamd.log, too:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53"><40985d>; task; rspamd_task_write_log: id: <undef>, qid: <95CE05A00547>, ip: 12.13.51.194, from: <…>, (default: F (no action): [3.40/15.00] [MISSING_MID(2.50){},MISSING_DATE(1.00){},MIME_GOOD(-0.10){text/plain;},ARC_NA(0.00){},ASN(0.00){asn:8220, ipnet:212.123.192.0/18, country:GB;},FROM_EQ_ENVFROM(0.00){},FROM_NO_DN(0.00){},RCPT_COUNT_ONE(0.00){1;},RCVD_COUNT_ZERO(0.00){0;},RCVD_TLS_ALL(0.00){},TO_DN_NONE(0.00){},TO_DOM_EQ_FROM_DOM(0.00){},TO_MATCH_ENVRCPT_ALL(0.00){}]), len: 181, time: 16.000ms real, 6.385ms virtual, dns req: 0, digest: <69b289a82827c11f759837c033cd800a>, rcpts: <…>, mime_rcpt: <…></span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="<40985d>; task; rspamd_task_write_log: id: <undef>, qid: <95CE05A00547>, ip: 12.13.51.194, from: <…>, (default: F (no action): [3.40/15.00] [MISSING_MID(2.50){},MISSING_DATE(1.00){},MIME_GOOD(-0.10){text/plain;},ARC_NA(0.00){},ASN(0.00){asn:8220, ipnet:212.123.192.0/18, country:GB;},FROM_EQ_ENVFROM(0.00){},FROM_NO_DN(0.00){},RCPT_COUNT_ONE(0.00){1;},RCVD_COUNT_ZERO(0.00){0;},RCVD_TLS_ALL(0.00){},TO_DN_NONE(0.00){},TO_DOM_EQ_FROM_DOM(0.00){},TO_MATCH_ENVRCPT_ALL(0.00){}]), len: 181, time: 16.000ms real, 6.385ms virtual, dns req: 0, digest: <69b289a82827c11f759837c033cd800a>, rcpts: <…>, mime_rcpt: <…>"><div></div></button></div></figure></div>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="autoexpunge">Autoexpunge</h2><a class="sl-anchor-link" href="#autoexpunge"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “Autoexpunge”</span></a></div>
|
||
<p>Andi Olsen pointed out that Dovecot has introduced an
|
||
<a href="https://doc.dovecot.org/2.4.2/core/config/namespaces.html#mailbox_autoexpunge">autoexpunge</a> feature to automatically
|
||
delete emails in a folder that reach a certain age. This is especially useful for the “Trash” and “Junk” folders. To
|
||
enable this feature, create yet another configuration file:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">cat</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/dovecot/conf.d/99-ispmail-autoexpunge.conf</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72"><<</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">'EOF'</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Remove mails from the Junk and Trash folders after 30 days</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">mailbox Junk {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">special_use = \Junk</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">auto = subscribe</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">mailbox_autoexpunge = 30d</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">mailbox Trash {</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">special_use = \Trash</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">auto = subscribe</span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#ECC48D;--1:#984E4D"> </span></span><span style="--0:#ECC48D;--1:#984E4D">mailbox_autoexpunge = 30d</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">}</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D"># Make expunging more efficient</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">mailbox_list_index = yes</span></div></div><div class="ec-line"><div class="code"><span style="--0:#ECC48D;--1:#984E4D">mail_always_cache_fields = date.save</span></div></div><div class="ec-line"><div class="code"><span style="--0:#D9F5DD;--1:#111111">EOF</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart Dovecot</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">reload</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">dovecot</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="cat > /etc/dovecot/conf.d/99-ispmail-autoexpunge.conf << 'EOF'# Remove mails from the Junk and Trash folders after 30 daysmailbox Junk { special_use = \Junk auto = subscribe mailbox_autoexpunge = 30d}mailbox Trash { special_use = \Trash auto = subscribe mailbox_autoexpunge = 30d}# Make expunging more efficientmailbox_list_index = yesmail_always_cache_fields = date.saveEOF# Restart Dovecotsystemctl reload dovecot"><div></div></button></div></figure></div>
|
||
<div class="sl-heading-wrapper level-h2"><h2 id="rspamds-web-interface">rspamd’s web interface</h2><a class="sl-anchor-link" href="#rspamds-web-interface"><span aria-hidden="true" class="sl-anchor-icon"><svg width="16" height="16" viewBox="0 0 24 24"><path fill="currentcolor" d="m12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z"></path></svg></span><span class="sr-only">Section titled “rspamd’s web interface”</span></a></div>
|
||
<p>rspamd comes with a neat bonus feature: a web interface. It allows you to check emails for spam, get statistics and
|
||
fine-tune scores. It is already installed and enabled by default and expects HTTP (not HTTPS!) requests on port 11334 on
|
||
the localhost interface. I suggest you add a simple proxy configuration to your already working HTTPS-enabled web mail
|
||
configuration to get access.</p>
|
||
<p><img src="/_astro/catching-spam-rspamd-dashboard.DTy1Y_zE_2jRE5g.webp" alt="rspamd dashboard" loading="lazy" decoding="async" fetchpriority="auto" width="1446" height="624"></p>
|
||
<p>You can either create a new virtual host configuration or just edit the
|
||
<code dir="auto">/etc/apache2/sites-enabled/000-default-le-ssl.conf</code> file. Anywhere within the <em>VirtualHost</em> tags add:</p>
|
||
<div class="expressive-code"><figure class="frame not-content"><figcaption class="header"></figcaption><pre data-language="plaintext"><code><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53"><Location /rspamd></span></div></div><div class="ec-line"><div class="code"><span class="indent"><span style="--0:#d6deeb;--1:#403f53"> </span></span><span style="--0:#d6deeb;--1:#403f53">Require all granted</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53"></Location></span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">RewriteEngine On</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">RewriteRule ^/rspamd$ /rspamd/ [R,L]</span></div></div><div class="ec-line"><div class="code"><span style="--0:#d6deeb;--1:#403f53">RewriteRule ^/rspamd/(.*) http://localhost:11334/$1 [P,L]</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="<Location /rspamd> Require all granted</Location>RewriteEngine OnRewriteRule ^/rspamd$ /rspamd/ [R,L]RewriteRule ^/rspamd/(.*) http://localhost:11334/$1 [P,L]"><div></div></button></div></figure></div>
|
||
<p>You also need to enable Apache’s modules for HTTP proxying and rewriting:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Enable the required Apache modules</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">a2enmod</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">proxy_http</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">a2enmod</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">rewrite</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart Apache</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">restart</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">apache2</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Enable the required Apache modulesa2enmod proxy_httpa2enmod rewrite# Restart Apachesystemctl restart apache2"><div></div></button></div></figure></div>
|
||
<p>This piece of configuration will forward any requests to <code dir="auto">https://webmail.example.org/rspamd</code> to localhost:11334 and
|
||
thus give you access to the rspamd web interface.</p>
|
||
<p>The web interface is password protected. Let’s generate a new access password, hash it and add it to rspamd’s
|
||
configuration:</p>
|
||
<div class="expressive-code"><figure class="frame is-terminal has-title not-content"><figcaption class="header"><span class="title">Run this on your server</span></figcaption><pre data-language="sh"><code><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Generate a random password that is 15 characters long</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">PW</span><span style="--0:#C792EA;--1:#8844AE">=</span><span style="--0:#D6DEEB;--1:#403F53">$(</span><span style="--0:#82AAFF;--1:#3B61B0">pwgen</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">15</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#F78C6C;--1:#AA0982">1</span><span style="--0:#D6DEEB;--1:#403F53">)</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Create a hash of that password</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">HASH</span><span style="--0:#C792EA;--1:#8844AE">=</span><span style="--0:#D6DEEB;--1:#403F53">$(</span><span style="--0:#82AAFF;--1:#3B61B0">rspamadm</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">pw</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#82AAFF;--1:#3B61B0">-p</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#C5E478;--1:#3B61B0">$PW</span><span style="--0:#D6DEEB;--1:#403F53">)</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Add the hashed password to rspamd's configuration</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">echo</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">password = </span><span style="--0:#F78C6C;--1:#AA0982">\"</span><span style="--0:#C5E478;--1:#3B61B0">$HASH</span><span style="--0:#F78C6C;--1:#AA0982">\"</span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#7FDBCA;--1:#096E72">></span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">/etc/rspamd/local.d/worker-controller.inc</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Restart rspamd</span></div></div><div class="ec-line"><div class="code"><span style="--0:#82AAFF;--1:#3B61B0">systemctl</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">restart</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#ECC48D;--1:#3B61B0">rspamd</span></div></div><div class="ec-line"><div class="code">
|
||
</div></div><div class="ec-line"><div class="code"><span style="--0:#919F9F;--1:#5F636F"># Show the password</span></div></div><div class="ec-line"><div class="code"><span style="--0:#C5E478;--1:#3B61B0">echo</span><span style="--0:#D6DEEB;--1:#403F53"> </span><span style="--0:#D9F5DD;--1:#111111">"</span><span style="--0:#ECC48D;--1:#984E4D">Please note the password for the web interface: </span><span style="--0:#C5E478;--1:#3B61B0">$PW</span><span style="--0:#D9F5DD;--1:#111111">"</span></div></div></code></pre><div class="copy"><div aria-live="polite"></div><button title="Copy to clipboard" data-copied="Copied!" data-code="# Generate a random password that is 15 characters longPW=$(pwgen 15 1)# Create a hash of that passwordHASH=$(rspamadm pw -p $PW)# Add the hashed password to rspamd's configurationecho "password = \"$HASH\"" > /etc/rspamd/local.d/worker-controller.inc# Restart rspamdsystemctl restart rspamd# Show the passwordecho "Please note the password for the web interface: $PW""><div></div></button></div></figure></div>
|
||
<p>This gives you a password like “sae8thaoTaengeo”. If everything went as expected you should now be able to access the
|
||
rspamd web interface at <code dir="auto">https://mail.example.org/rspamd</code> (of course you will use your own server name here).</p>
|
||
<p>You may miss the detailed history in the web interface. /etc/rspamd/local.d/redis.conf The content is: servers =
|
||
“127.0.0.1”;</p> </div> <footer> <footer class="sl-flex astro-3yyafb3n"> <div class="meta sl-flex astro-3yyafb3n"> <p>Last updated: <time datetime="2025-11-01T00:00:00.000Z">Nov 1, 2025</time></p> </div> <div class="pagination-links print:hidden astro-u2l5gyhi" dir="ltr"> <a href="/ispmail-trixie/managing-users-aliases-and-domains/" rel="prev" class="astro-u2l5gyhi"> <svg aria-hidden="true" class="astro-u2l5gyhi astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17 11H9.41l3.3-3.29a1.004 1.004 0 1 0-1.42-1.42l-5 5a1 1 0 0 0-.21.33 1 1 0 0 0 0 .76 1 1 0 0 0 .21.33l5 5a1.002 1.002 0 0 0 1.639-.325 1 1 0 0 0-.219-1.095L9.41 13H17a1 1 0 0 0 0-2Z"/></svg> <span class="astro-u2l5gyhi"> Previous <br class="astro-u2l5gyhi"> <span class="link-title astro-u2l5gyhi">Managing users, aliases and domains</span> </span> </a> <a href="/ispmail-trixie/anti-spoofing-dkim-spf/" rel="next" class="astro-u2l5gyhi"> <svg aria-hidden="true" class="astro-u2l5gyhi astro-c6vsoqas" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="--sl-icon-size: 1.5rem;"><path d="M17.92 11.62a1.001 1.001 0 0 0-.21-.33l-5-5a1.003 1.003 0 1 0-1.42 1.42l3.3 3.29H7a1 1 0 0 0 0 2h7.59l-3.3 3.29a1.002 1.002 0 0 0 .325 1.639 1 1 0 0 0 1.095-.219l5-5a1 1 0 0 0 .21-.33 1 1 0 0 0 0-.76Z"/></svg> <span class="astro-u2l5gyhi"> Next <br class="astro-u2l5gyhi"> <span class="link-title astro-u2l5gyhi">Prevent spoofing using DKIM and SPF</span> </span> </a> </div> </footer> <script defer src="https://comentario.workaround.org/comentario.js"></script>
|
||
<comentario-comments no-fonts="true" id="comments"></comentario-comments> <div class="myfooter">
|
||
This guide is maintained by <a href="mailto:ispmail@christoph-haas.de">Christoph Haas</a> since 2003. All content on workaround.org can be used under the terms of
|
||
the
|
||
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">Creative Commons BY-NC-SA license</a>.
|
||
Penguin image based on AI art because I suck at drawing.
|
||
</div> </footer> </div> </div> </main> </div> </div> </div> </div> </body></html> |