Security Azure

Apache SpamAssassin on Ubuntu 24.04 on Azure User Guide

| Product: Apache SpamAssassin on Ubuntu 24.04 LTS on Azure

Overview

This image runs Apache SpamAssassin, the long established open source anti spam platform, fully configured and security hardened so you can score mail within minutes of launch. The spamd daemon does the analysis, scoring each message against a large, regularly updated rule set that combines header and body heuristics, Bayesian statistical filtering, DNS blocklists, and SPF and DKIM checks, and the fast spamc client is what your mail system pipes messages through to get a verdict.

The image is secure by default. spamd runs as a dedicated non root system user (debian-spamd) and is bound to the loopback address (127.0.0.1) with its accept list restricted to 127.0.0.1, so nothing off the VM can reach it until you explicitly widen access to a mail host network you trust. A single management tool, sa-access, is the primary interface for that: it opens and closes the daemon to specific networks and refuses any world open exposure as a hard guardrail.

A current rule set is fetched at build time with sa-update, and the operating system maintenance timer is left enabled so every deployed VM keeps its rules fresh. On the first boot of every VM a one shot service records the server's details and the exact commands to open the daemon in a root only information file.

What is included:

  • Apache SpamAssassin spamd daemon + spamc client (Ubuntu 24.04 noble main), rules engine and sa-update
  • spamd running as the non root debian-spamd user, bound to 127.0.0.1:783 only (secure by default)
  • Loopback only accept list until you open it, managed with the sa-access tool (world open exposure refused)
  • Current rule set fetched at build; the spamassassin-maintenance timer left enabled for ongoing refresh
  • Per-VM first-boot initialisation writing server details to /root/spamassassin-info.txt
  • All packages from the Ubuntu archive (Apache-2.0 SpamAssassin) — clean open source, no third-party repository
  • 24/7 cloudimg support

Prerequisites

Before you deploy this image you need:

  • An active Microsoft Azure subscription where you can create virtual machines
  • An SSH key pair for administrator access to the VM
  • A virtual network and subnet in the target region. Mail servers reach spamd over the private network, so put the mail hosts that will use it in the same VNet (or a peered one)
  • A network security group (NSG) that allows inbound TCP 22 from your administrator network. spamd listens on TCP 783; only open that port, scoped to your mail host subnets, after you widen access with sa-access
  • The Azure CLI installed locally if you plan to deploy from the command line

Standard_B2s (2 vCPU / 4 GiB RAM) is a balanced default; size the VM to the volume of mail you will score.

Step 1: Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, and search the Marketplace for Apache SpamAssassin by cloudimg. Select the listing and choose Create.

On the Basics tab pick your subscription and resource group, a region, and a VM size (Standard_B2s or larger). Under Administrator account select SSH public key, set the username, and paste your public key. Under Inbound port rules allow SSH (22) for administration only; spamd stays loopback only until you open it. Select Review + create, then Create. First-boot initialisation completes within a few seconds of the VM reaching the Running state.

Step 2: Deploy from the Azure CLI

The following block creates a VM from the cloudimg Apache SpamAssassin image into an existing VNet and subnet. Replace the placeholders with your own values.

az vm create \
  --resource-group <your-rg> \
  --name spamassassin \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

Step 3: Connect as the Administrator over SSH

Connect to the VM with SSH as the administrator user you set at deploy time (azureuser by default on Ubuntu). The administrator account is SSH key only.

ssh azureuser@<vm-public-ip>

Step 4: Confirm the Daemon is Running

spamd starts automatically on boot. Confirm the service is active, that it runs as the non root debian-spamd user, that it is bound to the loopback address only, and that the configuration is valid:

sudo systemctl is-active spamd.service
ps -o user,comm -C spamd | sort -u
sudo ss -ltnp | grep ':783'
sudo spamassassin --lint && echo "configuration OK"

The process owner is debian-spamd (not root), and the listening socket is 127.0.0.1:783 — never 0.0.0.0:783. A clean --lint means the rule set loaded without error.

SpamAssassin status: the spamd service is active, running as the non root debian-spamd user, bound to 127.0.0.1:783 only, with a clean spamassassin --lint

Step 5: The Secure by Default Model

Read the first-boot information file and the exposure summary at any time. The file records the server's private IP and the exact commands to open spamd to your mail hosts.

sudo cat /root/spamassassin-info.txt
sudo sa-access status

The image ships with spamd listening on 127.0.0.1 and accepting connections from 127.0.0.1 only. This is deliberate: no host on the network can use the daemon until you explicitly allow its network. The image never ships a 0.0.0.0 listen or a 0.0.0.0/0 accept list.

The sa-access status view showing spamd active, listening on 127.0.0.1, an accept list of 127.0.0.1, and the first-boot server information file

Step 6: Test Spam Detection with GTUBE

GTUBE (the Generic Test for Unsolicited Bulk Email) is a standard string every spam filter is expected to score as spam. Pipe a message containing it through spamc and read the report:

GTUBE='XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X'
printf 'From: spammer@example.net\nTo: you@example.org\nSubject: test\n\n%s\n' "$GTUBE" | spamc -R

The report shows a score far above the default threshold of 5.0 and the GTUBE rule firing, and the returned message carries an X-Spam-Flag: YES header. This proves the daemon, the rule set and the spamc client are all working end to end.

Piping the standard GTUBE test string through spamc: the report shows a very high score, the GTUBE rule firing, and the message flagged as spam

Step 7: Confirm Clean Mail Passes

A filter is only useful if it lets legitimate mail through. Pipe a well formed clean message through spamc and confirm it scores below the threshold and is not flagged:

printf 'From: alice@example.com\nTo: bob@example.com\nSubject: Lunch tomorrow\nDate: Mon, 19 Jul 2026 09:00:00 +0000\nMessage-ID: <clean-001@example.com>\n\nHi Bob, are we still on for lunch tomorrow at noon? Thanks, Alice\n' | spamc -R

The report shows a low score, no X-Spam-Flag: YES header, and a verdict of not spam.

Piping a well formed clean message through spamc: the report shows a low score below the threshold and no spam flag, confirming legitimate mail passes

Step 8: Open spamd to Your Mail Hosts

To let a mail server on another host use spamd, allow its network with sa-access allow, passing a CIDR range or a single host. Use your own mail host subnet — the value below is only an example.

sudo sa-access allow 10.0.0.0/24
sudo sa-access status

The tool adds the network to the accept list, switches spamd to listen on all interfaces (it still only accepts the allow-listed networks), and reloads the daemon. You must also open TCP 783 to that network in your Azure NSG. Revert to loopback only at any time:

sudo sa-access loopback

World-open exposure is refused as a hard guardrail: sa-access allow '*' and sa-access allow 0.0.0.0/0 are both rejected, so you cannot accidentally expose the daemon to the whole internet.

Step 9: Integrate with Your Mail Server

spamd scores messages; your mail transfer agent decides what to do with the verdict. A common pattern with Postfix is to pipe inbound mail through spamc with spamass-milter, or as a simple content filter. For example, using the milter (install spamass-milter on the mail host and point it at this server):

# In Postfix main.cf on your mail host, referencing this SpamAssassin server:
smtpd_milters = inet:<server-private-ip>:783
non_smtpd_milters = $smtpd_milters
milter_default_action = accept

For a quick, transport-agnostic check from any host that can reach the daemon, spamc accepts the message on standard input and returns the scored message on standard output. Your MTA reads the X-Spam-Flag and X-Spam-Status headers it adds and files, quarantines or rejects accordingly. Keep the required_score threshold and any custom rules in /etc/spamassassin/local.cf.

Step 10: Keeping the Rule Set Current

SpamAssassin's effectiveness depends on current rules. The image fetches a fresh rule set at build time and leaves the operating system maintenance timer enabled so rules keep refreshing:

systemctl is-enabled spamassassin-maintenance.timer
sudo sa-update --version

You can refresh rules on demand at any time and reload the daemon:

sudo sa-update && sudo systemctl reload spamd.service

sa-update exits 0 when it installed new rules and 1 when the rules are already current — both are normal.

Step 11: Hardening Notes

The image is hardened out of the box: spamd runs as the non root debian-spamd user, listens on the loopback address only until you open it, and the sa-access tool refuses world-open exposure. Keep it locked down in production: when you widen access, scope the NSG rule for TCP 783 to only the mail host subnets that need it and never expose 783 to the internet; grant the narrowest CIDR ranges you can; keep the rule set current with the maintenance timer; and keep the OS patched (sudo apt update && sudo apt upgrade). Review scoring policy and any custom rules in /etc/spamassassin/local.cf.

Support

This image is backed by 24/7 cloudimg support. Contact us by email and chat for help with SpamAssassin configuration, spamd and spamc, integrating with Postfix or Exim, tuning the score threshold and rules, Bayesian training with sa-learn, and mail filtering architecture on Azure.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.