Skip to content

Fix rbl override check - #103

Open
AndrewSav wants to merge 2 commits into
mailserver2:masterfrom
AndrewSav:fix-rbl-override-check
Open

AndrewSav wants to merge 2 commits into
mailserver2:masterfrom
AndrewSav:fix-rbl-override-check

Conversation

@AndrewSav

@AndrewSav AndrewSav commented Nov 15, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Fixes #99. The second fault, a file not owned by root, has no issue of its own and is described below.

What rbl_override is

rbl_override is an optional Postfix access table for exempting mail servers from the zen.spamhaus.org blocklist check (README, "Whitelist Hosts/IP Addresses In Postfix"). The administrator writes a file of client IPs or host names, one per line, such as 1.2.3.4 OK, and bind-mounts it at /etc/postfix/rbl_override. smtpd_recipient_restrictions looks the client up with check_client_access hash:/etc/postfix/rbl_override just before reject_rbl_client zen.spamhaus.org, so an OK entry lets a listed client through. If nothing is mounted, setup.sh creates an empty file. Either way, setup.sh runs postmap on first start to build rbl_override.db, the file Postfix actually reads.

If the .db is missing, the lookup fails and Postfix answers RCPT TO with 451 4.3.5 ... Server configuration error. That hits every client that reaches the restriction, meaning anyone who is not authenticated and not in mynetworks. In practice, that is all mail from the internet.

  • What is the current behavior (you can also link to an open issue here) ?

    1. rbl_override is a directory (postfix rbl override check has a typo #99). Docker creates a directory when the host file of a bind mount is missing. setup.sh meant to catch this, but three faults stopped the check from working:

      • It tested /etc/postifx/rbl_override, so it never fired. Setup reported creating a default file, skipped postmap, and Postfix had no map.
      • The error message put the path in backticks inside double quotes, so the shell tried to run it.
      • exit 1 only ended setup.sh. run.sh ignores its exit status, so the services started anyway, with the rest of setup skipped.
    2. rbl_override is not owned by root. The administrator creates rbl_override on the host. If a regular user creates it, the bind mount keeps that user's UID, so inside the container the file is owned by a non-root UID too. setup.sh runs postmap as root inside the container, to create rbl_override.db next to the mounted file in the container's /etc/postfix. By default, postmap(1) drops root privileges and runs as the owner of a non-root input file, so here it runs as that UID. /etc/postfix is root:root 755, so postmap can't create the .db there. setup.sh didn't check postmap's exit status:

      [INFO] /etc/postfix/rbl_override file found
      postmap: fatal: open database /etc/postfix/rbl_override.db: Permission denied
      [INFO] RBL Override initialized
      

      Nothing in the README says the file must be owned by root, so following it as a regular user is enough to hit this. Reproduced on a build of master (bd09d3b) with a 1.2.3.4 OK file owned by a regular user: RCPT TO from outside mynetworks got 451 4.3.5. Running postmap -o /etc/postfix/rbl_override and postfix reload in that container turned it into 250 2.1.5 Ok. sender_access doesn't have this problem, because setup.sh copies it into /etc/postfix first, so postmap reads a copy owned by root.

  • What is the new behavior (if this is a feature change) ?

    • The directory check tests the right path, prints it, and marks /etc/setup-error like the other setup checks, so run.sh stops the container.
    • setup.sh runs postmap -o, which keeps root privileges, so the owner of the file no longer matters. Postfix still logs warning: not owned by root: /etc/postfix/./rbl_override at start. That warning comes from its own ownership check on the source file, and the map works.
    • If postmap fails for any reason, setup.sh logs [ERROR] Could not build the rbl_override map and marks /etc/setup-error, so the container stops instead of deferring mail.
    • Test plumbing: the two "startup stops" tests in default.bats start their own container, so they need an image name. run_default now passes NAME to them as MAILSERVER_IMAGE, so make default NAME=... tests that image rather than whatever :testing holds. A plain make default, as in CI, behaves as before.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Test (adding missing tests or correcting existing ones)

Status

  • Ready

Todo List

  • Implementation
  • Tests

How has this been tested ?

Four new tests. The first, third and fourth fail on a build without the fixes and pass with them. The second guards the default path, which was already working:

  • default: "startup stops when rbl_override is a directory" mounts a tmpfs at /etc/postfix/rbl_override and expects the container to exit 1 with the error.
  • default: "rbl_override map is built when none is provided" checks that the default start still builds the map.
  • default: "startup stops when the rbl_override map cannot be built" mounts a directory where rbl_override.db goes, so postmap fails, and expects the container to exit 1 with the error. Without the fix, postmap logged fatal: open /etc/postfix/rbl_override.db: Is a directory, setup reported RBL Override initialized, and the services started.
  • reverse: "rbl_override map is built from a file not owned by root" bind-mounts test/share/postfix/rbl_override from the checkout, so it belongs to the user running the suite. The test first checks that this owner isn't root, then looks up 192.0.2.1 in the built map and expects OK. Without the fix, the container logged the Permission denied lines above, and the lookup failed because rbl_override.db did not exist.
  • All nine suites pass on this branch (on master caa2b58), 358/358: default 139, reverse 85, ldap 66, ldap2 30, sieve 2, ecdsa 4, traefik_acmev1 11, traefik_acmev2 12, traefik_v3 9.
  • Against an image built from master before this branch (38c6cd3), both "startup stops" tests fail: the container keeps running until the test's 180-second timeout. For the directory case, setup logs Created default (empty) rbl_override file.

A bind mount whose host file is missing makes Docker create a directory
in its place. setup.sh meant to catch that, but three faults kept it
from working:

- The check tested /etc/postifx/rbl_override, so it never fired. Setup
  then reported creating a default file, skipped postmap, and Postfix
  answered unauthenticated clients' RCPT with 451 4.3.5 for want of the
  map.
- The error message put the path in backticks inside double quotes, so
  the shell tried to run it and the message printed without the path.
- exit 1 only ended setup.sh. run.sh ignores its exit status, so
  services started with the rest of setup skipped.

The check now looks at the right path, prints it, and marks the error
in /etc/setup-error as the other setup checks do, so run.sh stops the
container. The default-file branch is unchanged.

Two tests: a container with a directory at that path must exit 1 with
the error, and a default start must still build the map.

Closes mailserver2#99
The administrator creates rbl_override on the host. If a regular user
creates it, the bind mount keeps that UID, so inside the container the
file is owned by a non-root UID. setup.sh ran postmap on it as root, but
postmap drops root privileges and runs as the owner of a non-root input
file unless given -o. As that UID it could not create rbl_override.db in
/etc/postfix, which is root:root 755. setup.sh ignored the exit status
and logged "RBL Override initialized", and Postfix answered
unauthenticated clients' RCPT from outside mynetworks with 451 4.3.5.

setup.sh now runs postmap -o, and a failed postmap marks
/etc/setup-error, so run.sh stops the container instead of starting
Postfix without the map.

Two tests:
- reverse bind-mounts an rbl_override owned by the user running the
  suite, checks that this is not root, and looks up an entry in the
  built map.
- default starts a container with a directory where the .db goes and
  expects startup to stop with the error.

The startup tests in default.bats start their own container and named
the :testing image directly. run_default now passes NAME to them as
MAILSERVER_IMAGE, so make default NAME=... tests that image.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

postfix rbl override check has a typo

2 participants