Debian GNU/Linux · Services

Restrict the mail transfer agent to local delivery

When it fails: The mail transfer agent accepts connections from the network. An MTA listening on external interfaces can be abused as a relay or attacked remotely.

CB-DEB-SVC-010mediumactiveautomatedautomated fixprofile core

Description

Most systems only need a mail transfer agent (MTA) to send local notifications (cron output, security alerts) to an administrator. Such an MTA must listen on the loopback interface only, on the SMTP ports 25, 465 and 587.

On Debian the MTA is usually exim4 (dc_local_interfaces in /etc/exim4/update-exim4.conf.conf) or Postfix (inet_interfaces in /etc/postfix/main.cf). Sendmail, harder to configure safely, must not be installed.

The control applies only when an MTA is installed (/usr/sbin/sendmail exists). Debian's exim4 default configuration already listens on loopback only. The fix restarts exim4 or Postfix itself, and only when it changed their configuration.

Rationale

An MTA reachable from the network can be turned into an open relay for spam or phishing, and exposes a complex parser to remote attackers. Local notifications work just as well through the loopback interface.

Impact

The system no longer receives mail from other hosts. Mail servers and relays must exclude this control in their tailoring file. Removing sendmail-bin also removes packages that depend on it; install exim4 or Postfix first if the system must still send mail.

Applies when

Detected: when: A mail transfer agent is installed A scanner reports the control not applicable when one is false.

Audit

  1. Run ss -Hltn. No socket on port 25, 465 or 587 may be bound to an address other than 127.0.0.0/8 or ::1.
  2. Run dpkg-query -W -f='${Status}\n' sendmail-bin. It must not print install ok installed.

Automated: every check must pass. Scanners report each check by its id.

loopback-only

SMTP ports are bound to loopback addresses only.

script: |
  command -v ss >/dev/null 2>&1 || { echo "ss not found"; exit 3; }
  o=$(ss -Hltn 2>/dev/null | awk '{ print $4 }' | grep -E ':(25|465|587)$' |
    grep -Ev '^(127\.[0-9.]+(%[a-z0-9]+)?|\[::1\]|\[::ffff:127\.[0-9.]+\]):')
  if [ -n "$o" ]; then
    echo "SMTP listening on:" $o
    exit 1
  fi
  echo "no SMTP port bound outside loopback"

sendmail

Sendmail MTA package.

name: sendmail-bin
state: absent

Remediation

  • exim4: set dc_local_interfaces='127.0.0.1 ; ::1' in /etc/exim4/update-exim4.conf.conf, then run update-exim4.conf and systemctl restart exim4.
  • Postfix: run postconf -e 'inet_interfaces = loopback-only' then systemctl restart postfix.
  • Sendmail: install exim4 or Postfix, then apt purge sendmail-bin.
Restartnone
Disruptionlow

Rollback

Restore the configuration file from the backup directory, then run update-exim4.conf and restart the MTA.

loopback-only

# Bind the local MTA (exim4 or Postfix) to the loopback interface only.
# Idempotent: each MTA is changed and restarted only when its setting differs.
rc=0
f=/etc/exim4/update-exim4.conf.conf
want="dc_local_interfaces='127.0.0.1 ; ::1'"
if [ -f "$f" ] && ! grep -qxF "$want" "$f"; then
  cb_backup "$f"
  if grep -q '^dc_local_interfaces=' "$f"; then
    sed -i "s/^dc_local_interfaces=.*/$want/" "$f"
  else
    printf '%s\n' "$want" >>"$f"
  fi
  if update-exim4.conf; then
    systemctl try-restart exim4.service || rc=1
  else
    cb_restore "$f"
    echo "update-exim4.conf failed, reverted $f" >&2
    rc=1
  fi
fi
if command -v postconf >/dev/null 2>&1 && [ -f /etc/postfix/main.cf ] &&
  [ "$(postconf -h inet_interfaces)" != loopback-only ]; then
  cb_backup /etc/postfix/main.cf
  postconf -e 'inet_interfaces = loopback-only' || rc=1
  systemctl try-restart postfix.service || rc=1
fi
exit "$rc"

sendmail

Generated from check sendmail: the engine writes the fix for every output, it sets the expected value and the check runs again after.

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Not applicable on fresh image (no MTA); Postfix on 12 and exim4 on 13 listening on all interfaces fail, fix binds them to loopback, audit passes after reboot; second remediate leaves the MTA untouched.
Verified2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Not applicable on fresh image (no MTA); Postfix on 12 and exim4 on 13 listening on all interfaces fail, fix binds them to loopback, audit passes after reboot; second remediate leaves the MTA untouched.
Introduced inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr