seraph-tool.sh
Command reference for installing and managing the DirectAdmin integration. For initial setup, follow the DirectAdmin guide.
On this page
Version 12.
seraph-tool.sh prepares a DirectAdmin server with Exim and Dovecot to receive its mail through
Seraph. Run it as root on that server. install does it all in one go (below); it keeps a copy in
/usr/local/seraph, linked as /usr/local/sbin/seraph-tool.sh, so later commands work by the bare
name. (With sudo on AlmaLinux, whose secure_path leaves out /usr/local/sbin, use the full
path /usr/local/seraph/seraph-tool.sh.)
Upgrading from INBOX.spam to top-level Junk? Follow the
migration runbook before installing the updated plugin:
installation changes future default delivery; migrating existing mail remains a separate operation.
- It asks before every change.
--yesanswers yes for you. - It backs up what it changes. If Exim or Dovecot refuses a change, it puts the backup back.
- It logs every change in
/usr/local/seraph/actions.log. - It refuses to run from a file someone other than root could change or replace.
- Nothing changes for a domain until its MX points at Seraph.
Exim needs DirectAdmin's hooks for its ACLs, including the one for mail sent on the server itself
(/etc/exim.acl_script.pre.conf). Setup refuses without it: a forged verdict in such mail would be
trusted. da build exim_conf gives an older exim.conf the hooks.
Dovecot 2.3 or 2.4 needs Pigeonhole for Sieve. This shows if CustomBuild has it on:
grep -E '^(dovecot|dovecot_conf|pigeonhole)=' /usr/local/directadmin/custombuild/options.conf
All three should say yes. If pigeonhole says no, these lines switch it on and rebuild Dovecot:
da build set pigeonhole yes
da build dovecot
The build takes a few minutes of CPU. Then Dovecot restarts, so IMAP and POP3 clients reconnect.
If dovecot or dovecot_conf says no, CustomBuild doesn't manage Dovecot on this server. Then
install Pigeonhole for that Dovecot yourself. --install-deps won't do it there.
What it changes
The tool only uses files that survive da build and DirectAdmin updates. Some files are shared
with DirectAdmin or with you. In those, it only changes what's between its own # BEGIN SERAPH
and # END SERAPH lines.
| File | For |
|---|---|
/etc/exim.variables.conf.post |
the host lists: Seraph, and your old filter during the switch; the domains the port-25 lock covers |
/etc/exim.acl_check_recipient.pre.conf |
header trust and the port-25 lock |
/etc/exim.acl_script.pre.conf |
header trust for mail sent on the server itself |
/etc/virtual/seraph_hosts_ip, seraph_transition_ip |
Seraph's hosts and your old filter's addresses |
/etc/virtual/seraph_locked_domains |
the domains the port-25 lock covers, as the control plane lists them |
/etc/virtual/seraph_fallback_ip |
a delivery provider's addresses, let through the port-25 lock while a domain here uses it as fallback |
/etc/virtual/seraph_relay_senders |
after outbound on: the senders Seraph relays |
/etc/virtual/whitelist_hosts, whitelist_hosts_ip |
DirectAdmin's whitelist, with --whitelist yes |
/etc/dovecot/conf.d/99-seraph.conf |
the Sieve rule that puts junk in the spam folder |
/etc/dovecot/conf.d/00-seraph-learn.conf |
learning from the spam folder, after learn on |
/etc/dovecot/conf.d/99-seraph-learn-imap.conf |
on Dovecot 2.3: the IMAP plugin learning needs |
/etc/exim.routers.pre.conf, exim.transports.pre.conf, exim.authenticators.post.conf |
after outbound on: outbound mail through Seraph's relay |
/etc/exim.seraph_relay |
after outbound on: the server's relay secret, readable by root and Exim's group only |
/usr/local/seraph/relay.id |
after outbound on: whose relay secret /etc/exim.seraph_relay is |
/etc/sudoers.d/seraph-plugin, /usr/local/libexec/seraph-plugin, seraph-plugin-events, /etc/cron.d/seraph-plugin |
the plugin's root helper, its sudo rule and its background run |
/etc/systemd/system/seraph-learn-janitor.timer |
learning's check every 15 minutes: old samples out, room for new ones |
/etc/systemd/system/seraph-learn-upload.timer |
with personal learning: corrections to the control plane every 2 minutes |
/etc/systemd/system/seraph-refresh.timer |
after enrol: Seraph's delivery hosts from the control plane every 5 minutes |
/usr/local/seraph/ |
the tool's own copy, Sieve scripts, seraph-learn, receiver.conf, backups, actions.log, samples and exports |
/usr/local/seraph/client/ |
after enrol: the control plane's PHP client, client.php and cli.php |
/usr/local/seraph/cp/ |
after enrol: this server's token and the control plane's address, root's, readable by the system user seraph alone (its cache is there too) |
/usr/local/sbin/seraph-tool.sh |
a link to the copy in /usr/local/seraph, for the bare name; a file of yours there stays |
the system user seraph |
after enrol: runs the plugin and the control plane's client, without a shell or a login |
The scheduled jobs are systemd timers (each with its .service), and each service runs sandboxed:
the system read-only but for what that job writes, no new privileges, only the capabilities and
address families it needs. The janitor has no network; refresh may write in /etc/virtual and
/usr/local/seraph only. Where systemd isn't running (a container), they are cron jobs in
/etc/cron.d/seraph-learn and seraph-hosts instead; setup moves older cron jobs to timers.
install
The one path for a server with a Seraph account: from nothing to connected, set up, the plugin installed and outbound mail through Seraph.
Seraph sends you a one-line command and a code. Run the command as root on the server and type the
code when it asks for it (not shown as you type; or put it in SERAPH_ENROL), never on the command
line. It works once, within 24 hours. The command checks the code with Seraph before anything
changes, then fetches this tool, checks it, and runs seraph-tool.sh install --cp <the control
plane>.
install runs every step from a copy of the fetched file in a folder of root's, a line each: ok,
skip with why, or FAIL with why and what to run.
The first FAIL stops it; fix that and run it again.
- preflight: root, DirectAdmin, Exim with its hooks, Dovecot with Pigeonhole, PHP with json and
curl or openssl. Everything
setupandenrolwould refuse, at once, before anything changes. - enrol: skipped while the control plane takes this server's token. When it no longer does (revoked there), it asks for a new code.
- setup with the hosts and key from the control plane (
setup --from-cp). - plugin:
plugin install, skipped when that package is installed already. - outbound:
outbound on, skipped until Seraph has set the server up for its relay (a FAIL when outbound mail already goes to the relay here: it waits until Seraph has, or untiloutbound off). - report: the server's state to the control plane, then
check(a refusal there is a FAIL too).
| Option | |
|---|---|
--cp <url> |
the control plane; afterwards the one in /usr/local/seraph/cp |
--without plugin|outbound |
leave a step out (a comma list, or the option twice) |
Run it again any time (a server set up with --whitelist no keeps that): it never enrols while the token works, and setup, plugin and outbound change
nothing (and reload neither Exim nor Dovecot) when nothing changed. A run stopped before it reloaded Exim or Dovecot
leaves that in /usr/local/seraph/reload-pending, so the next one reloads them anyway. The plugin
package brings its own copy of the tool; the one install ran keeps its place (with a warning when
the versions differ). If the answer to the enrolment never arrives, the code may be spent: run
install again with it, and ask Seraph for a new one if the control plane says it is used. The tool's sha256 and the plugin's version and
sha256 go to actions.log. If the control plane enrolled the server but its answer can't be kept,
the line says so: ask Seraph for a new code and run install again. If /usr/local/seraph/cp can't
be written, nothing is sent and the code still works.
check
Shows what's set up. It changes nothing. It lists:
- versions, Pigeonhole and the spam folder;
- if Seraph can verify this server's TLS certificate;
- Seraph's hosts, the whitelist, header trust, junk filing, the lock and learning;
- where each domain's MX points;
- any local spam filtering that also runs.
seraph-tool.sh check
outbound
Sends the server's outbound mail through Seraph's relay, which checks and counts it and passes it
on through a delivery provider such as MailChannels, or delivers it itself. Seraph sets the server
up for its relay when it enrols; the server then sends as its own domains and its own name. install turns it on.
seraph-tool.sh outbound on
seraph-tool.sh outbound status
seraph-tool.sh outbound off
on names the domains on the server that have no way out at Seraph yet: they keep their old path
until Seraph routes them (a new domain once its SPF include and provider records are seen). It
refuses until Seraph has set the server up for its relay (ask Seraph). It fetches the server's secret once (outbound rotate fetches a new one, and so does a new enrolment
or a change of control plane or login: /usr/local/seraph/relay.id says whose it is) and routes mail from the server's own domains and name, for
other servers, to the relay: over port 587, TLS with the relay's certificate checked, logged in.
It goes ahead of a smarthost you had in /etc/exim.routers.pre.conf, and on refuses while a
router in exim.conf itself would still take the mail first. DirectAdmin's DKIM signing and sending
limits apply as before. The relay is told who sent each message: the mailbox that logged in to
Exim, or the local user for scripts. A sender can't set that themselves.
While Seraph defers or can't be reached, the mail waits on the server and Exim retries it; it never
goes another way. Mail the server makes itself (bounces, with an empty sender), and forwards that
keep the original outside sender (DirectAdmin without SRS), go as they did before. off puts the server back as it was, and is the way back should Seraph's relay
be down for long.
The port-25 lock
For a locked domain, port 25 takes mail only from Seraph, localhost and logged-in users. Mail clients keep using ports 587 and 465. Other hosts get a temporary refusal (451), never a bounce: a domain locked too early, or a Seraph node this server doesn't know yet, delays mail.
Each domain locks by itself. setup installs the lock; the control plane lists a domain 24 hours
after it sees the domain's MX at Seraph alone (senders may still have the old MX until then), and
refresh writes that list every 5 minutes. Nobody runs a command. A domain leaves the list at
once when its MX moves away, its claim goes, or Seraph keeps it open at your request. The plugin's Restore takes the domain off the list here
as it writes the old MX back. refresh keeps the list as it is when the control plane can't be
reached or sends names that don't read as domains; it fetches Seraph's delivery hosts the same
way. check shows the list's size and when the control plane last confirmed it.
seraph-tool.sh lock --transition "<old filter's addresses>"
seraph-tool.sh lock
seraph-tool.sh unlock
seraph-tool.sh unlock --domain <domain>
locklocks every domain at once. It refuses while any domain's MX still points somewhere else, and names those domains.--forcelocks anyway.--transitionkeeps accepting mail from your old filter while DNS caches expire. Some days later, runlockagain without it.unlocklifts the lock for every domain. The domains the control plane lists stay locked.unlock --domain <domain>is the break-glass for one domain: its mail from anyone comes in here at once. The nextrefreshlocks it again while the control plane still lists it, so also ask Seraph to keep it open, or move its MX.
learn
Learning from the spam folder. Users already move spam into the spam folder. With learning on, your server keeps a copy of that mail for Seraph, which uses it to make the filter better. Learning is off until you switch it on, for each domain.
seraph-tool.sh learn on example.nl # spam: mail moved into the spam folder
seraph-tool.sh learn on example.nl --ham yes # good mail too: mail moved out of it
seraph-tool.sh learn # what learns, and what waits to be sent
seraph-tool.sh learn export # the samples, encrypted, for Seraph
seraph-tool.sh learn off example.nl
- Spam. When a user moves or copies mail into the spam folder, a copy is kept as a spam sample. The message itself stays where the user put it.
- Good mail, only with
--ham yes. When a user moves mail out of the spam folder, a copy is kept as a good-mail sample. Mail moved to the bin isn't kept.--ham noswitches this off again, and deletes the good-mail samples you haven't sent yet. exportputs the samples in one file:/usr/local/seraph/samples-<server>-<date>.seraph. The file is encrypted with Seraph's public key (setup --key), so only Seraph can open it. Then the samples are deleted from the server. Send the file to Seraph. This needs openssl 1.1.1 or later.offstops learning for those domains. It also deletes their samples you haven't sent yet.
Personal learning is separate. A mailbox can have its own filter that learns from its user's
corrections. Its user turns it on in the DirectAdmin plugin ("Learn from corrections"), and the
plugin runs seraph-tool.sh learn personal sync <domain>, which asks Seraph who learns. Only
those mailboxes' moves into and out of the spam folder are kept, with the mailbox
(/usr/local/seraph/learn-personal.conf lists them). Every 2 minutes learn upload (a timer; cron
in a container) asks
again and sends them to Seraph; one whose mailbox turned off meanwhile is not sent. They are then
deleted, unless the domain also shares that kind of mail with Seraph (learn on); then the next export
includes them too, without the mailbox. learn personal on|off <domain> does the same by hand. It
uses the same Dovecot rules as learn on.
Samples are personal data. Please read this first:
- Samples stay in
/usr/local/seraph/learn/<user>/until you send them. Samples you don't send are deleted after 30 days. - Root and the domain's owner in DirectAdmin can read them. That user can already read the mail itself.
- Spam learning keeps mail that users mark as spam. Sometimes a user moves a personal message there by mistake. Some mail apps also move mail there by themselves. Those copies are kept too.
- Good-mail learning keeps real messages, with their attachments. Users may not see moving a message as reporting it. Only switch it on if your agreement with the users allows it, and tell them first.
How learning stays out of the way of mail:
- It adds one Dovecot file:
/etc/dovecot/conf.d/00-seraph-learn.conf. On Dovecot 2.3 there's a second one,99-seraph-learn-imap.conf, which only adds the IMAP plugin learning needs. Your own Dovecot settings stay as they are. That includes your IMAP plugins, your Sieve plugins and any IMAPSieve rules. - The first file's name sorts first. So if you later add a Dovecot setting that clashes with it,
yours wins.
checkandlearnthen say that learning isn't working, and why. - If Dovecot already has its own
sieve_pipe_bin_dir, theseraph-learnprogram goes in there. Nothing else in that folder changes. - Before Dovecot reloads, the tool checks that the Pigeonhole plugins it needs are there. It also starts IMAP once with the new settings. If anything fails, it puts every file back.
- Mail delivery doesn't change. Learning only runs when a user moves or copies mail over IMAP. Junk that Seraph put in the spam folder doesn't count.
- If a copy can't be kept, the user's move still works. For example when the disk is full, or the message is larger than 2 MB.
- To undo it by hand, delete the
seraph-learnfiles in/etc/dovecot/conf.d/and rundoveadm reload.
Samples must never fill a disk or a quota. They're files of the domain's owner in DirectAdmin, but
they count towards that user's disk quota only where the quota covers /usr/local (one filesystem
for / and /home). So learning keeps nothing for a DirectAdmin user while:
- 50 MB or 2000 samples of theirs wait to be sent;
- the disk has less than 1 GB or 5% free;
- their disk quota has less than 200 MB (or 1000 files) left, or the quota can't be read.
A light job checks this every 15 minutes (a timer; cron in a container), at the lowest CPU and disk
priority. It also
deletes samples older than 30 days (by when they were written, which a user can't change), whether
or not anyone uses learning. A user's move never waits for it. learn shows for whom nothing is
kept, and why.
One limit on Dovecot 2.3: when learning has to add to sieve_plugins or
sieve_global_extensions, it copies your current values into its own file. If you later change
them in dovecot.conf itself, learning's file keeps the old ones. Change them in a conf.d file
instead, or run learn off and learn on again.
enrol, domains, settings and senders
With these, Seraph's control plane manages this server's domains. install enrols; enrol alone
does only that step. Seraph gives you an enrolment code, which works once, within 24 hours. Type it
at the prompt, or put it in SERAPH_ENROL; never on the command line. To enrol again after Seraph
revoked the server, run seraph-tool.sh install and type the new code.
seraph-tool.sh enrol --cp <the control plane's address, from Seraph>
Before it sends the code, enrol checks that it can write the enrolment: a server that can't keep
its token never uses up a code.
enrol creates a system user, seraph, without a shell or a login. Root writes this server's own
token and the control plane's address in /usr/local/seraph/cp; seraph can read them, not change
them. So where root sends the token (refresh, plugin install) is never that user's choice.
seraph runs the PHP client: DirectAdmin's /usr/local/bin/php, else
the system's, without php.ini. The client uses curl when PHP has it, else PHP's own https
streams, so it needs either curl or openssl. Enrolling tells Seraph the tool's version, whether
header trust is on, how the lock covers the domains (every domain, or the list, by its sha256), and
the PHP version; setup, lock, unlock and a refresh that changes the list tell it again.
seraph-tool.sh domains push example.nl,shop.example.nl # or all; each waits for Seraph to approve it
seraph-tool.sh domains alias example.com example.nl # example.com's mail is example.nl's
seraph-tool.sh domains # each domain: waiting, active, live, its MX
seraph-tool.sh domains checks example.nl # what still has to happen, and how
seraph-tool.sh domains check example.nl # the same, with the DNS looked at now
seraph-tool.sh domains dns example.nl # the records Seraph wants, and what DNS has
seraph-tool.sh settings example.nl # each setting, and where it comes from
seraph-tool.sh settings info@example.nl sensitivity=strict classes.newsletter=junk
seraph-tool.sh settings info@example.nl sensitivity= # inherits from the domain again
seraph-tool.sh senders example.nl allow partner.example # or block; unlist allow|block takes it off
seraph-tool.sh status
A push sends each domain's MX as it is now: domains dns lists the records Seraph wants by phase
(the ownership TXT while it waits for approval, MX, the SPF include and a provider's record) with
what DNS has for each (found, missing, or another value), what is left out and why, the
nameservers it asked, and the records from before, if the domain leaves Seraph. domains check
asks the control plane to look at the domain's DNS now (at most every 20 seconds) and prints the
checks. Change the MX only once domains dns says the domain is live. If the domain has an MTA-STS
policy, update the policy first.
The plugin points records only into Seraph's DNS zones. seraph-tool.sh dns-zones list shows them,
and dns-zones set <zone ...> sets them, as root. Setup offers the list in its confirmation, from
the delivery hosts' zones and the control plane's; refresh never writes it; a public suffix such
as co.uk is never a zone; and check names what the control plane uses that the list lacks.
When the control plane can't be reached, settings, senders, domains and status show what
they last saw and say so. A change then fails and changes nothing. remove deletes the token, the
client, the plugin's sudo rule and root helper, and then the seraph user. Seraph still lists the
server until it revokes it.
setup
Sets up the server for Seraph. Run it again to update an earlier setup; when nothing changes, it reloads nothing.
seraph-tool.sh setup --from-cp
- Exim trusts
X-Seraph-*headers only on mail from Seraph. It removes them from all other mail. When a hostname of Seraph's can't be looked up (DNS trouble), its mail counts as not Seraph's: the headers go, and under the lock the mail waits. DirectAdmin's SpamAssassin hands mail back to Exim asspam-scanned, which only a trusted caller can set; that mail keeps Seraph's verdict. - A Dovecot Sieve rule puts mail that Seraph marked as junk in the spam folder. That folder is
INBOX.spam, orJunkwhen DirectAdmin'sspam_inbox_prefixis 0. - The rule acts on
X-Seraph-Actiononly.X-Seraph-Evidence(what decided, and any check Seraph couldn't do this time),X-Seraph-Class(phishing,spam,cold-outreach,legitimateoruncertain) andX-Seraph-Reasonare there for your own filters and for people. - With
--whitelist yes, DirectAdmin's own RBL and spam checks skip mail from Seraph. Mail for domains that aren't behind Seraph is still checked. --keysaves Seraph's public key in/usr/local/seraph/receiver.conf. It's used to encrypt samples for Seraph (seelearn). If you run setup again without--key, the saved key stays.- Setup keeps a copy of itself in
/usr/local/seraph/seraph-tool.sh. Later commands use that copy, so you don't download the tool again.
| Option | |
|---|---|
--seraph "<hosts>" |
Seraph's delivery hosts, as hostnames or addresses |
--from-cp |
read them from the control plane this server is enrolled with (install does this) |
--key <key> |
Seraph's public key, for learn export. --from-cp takes it from the control plane |
--whitelist yes|no |
let DirectAdmin's own checks skip Seraph's mail. The default is yes, or what a server set up before has |
--install-deps |
if Pigeonhole is missing, run the two da build lines above, after asking. Only where CustomBuild already manages Dovecot and its configuration |
Without options, setup asks for the hosts. On an enrolled server, --from-cp takes the hosts and
the key from the control plane, as install does.
remove
Removes everything again: header trust, junk filing, the lock, outbound through Seraph, the
whitelist entries and learning.
Samples you haven't sent are deleted. Run learn export first to keep them.
seraph-tool.sh remove
seraph-tool.sh remove --purge
remove keeps the backups, the log and earlier exports in /usr/local/seraph. --purge also
deletes /usr/local/seraph, the link in /usr/local/sbin and the script itself, once Exim and
Dovecot run without Seraph.
Nothing of the tool is left.
Stop filtering every domain in the plugin before you remove it.
DirectAdmin plugin
The Mail Filter plugin adds admin, reseller and user pages for domains, filtering, sender lists,
readiness checks, DNS and learning. It reuses this server's enrolment automatically. install
installs it; to install or update it alone, with the package from the control plane:
seraph-tool.sh plugin install
It is skipped when that package (its version, and its sha256 kept beside it) is installed already;
plugin install --force installs it again anyway. The package is unpacked under /usr/local/seraph
(never /tmp, which may be noexec); a failure says what its installer printed.