Hi,

  Following up on my earlier question. After digging into the Postfix source   and documentation more carefully I believe I have a clearer picture, but I   would like to confirm two candidate configurations before changing production.

  --- What I now understand ---

  TCP lookup tables (tcp:) send the full recipient address to the daemon exactly   once. They do not attempt the multi-form lookups (user+ext@domain, user@domain,   @domain) that indexed maps perform. This means my TCP daemon on port 9926 is   solely responsible for handling plus-addressing, catchalls, and alias-domain   canonicalisation — Postfix will not retry with a stripped or rewritten key.

  Our daemon already does this:
    - Step 1: resolves alias domains in-process (no additional DB round-trip)
              and checks for mailbox existence.
    - Step 2: checks for explicit aliases and @domain catchalls.

  So for any recipient in a virtual mailbox domain, by the time
  smtpd_reject_unlisted_recipient would trigger virtual_mailbox_maps, our daemon   has already answered "mailbox does not exist" using the same table and the same
  conditions. The virtual_mailbox_maps lookup would always return nothing.

  I also verified that we deliver outbound via SMTP to a relay
  (virtual_transport = smtp:[relay]:587), not via Postfix virtual(8).
  This means virtual_mailbox_maps is consulted only during SMTP-time recipient
  validation, not at delivery time for mailbox path resolution. A no-op
  replacement should therefore be safe.

  One thing I want to confirm: setting virtual_mailbox_maps = <empty> while
  keeping smtpd_reject_unlisted_recipient = yes does NOT give us what we want —   it disables the recipient check entirely rather than rejecting unknowns. So an
  empty map is not the right answer.

  --- Two candidate designs ---

  Design A — keep virtual_alias_maps for rewriting, replace virtual_mailbox_maps
             with a cheap no-op TCP service:

    virtual_alias_maps          = tcp:127.0.0.1:9926
    virtual_mailbox_maps        = tcp:127.0.0.1:9927   # always returns NOT_FOUND
    smtpd_reject_unlisted_recipient = yes

    Port 9926 handles both alias rewriting and implicit mailbox validation.
    Port 9927 exists only so Postfix has a non-empty virtual_mailbox_maps; it
    returns NOT_FOUND for every query without touching the database.
    smtpd_reject_unlisted_recipient does the rejection.

  Design B — move accept/reject into smtpd_recipient_restrictions, remove
             virtual_mailbox_maps entirely:

    smtpd_reject_unlisted_recipient = no

    smtpd_recipient_restrictions =
        permit_mynetworks
        permit_sasl_authenticated
        reject_unauth_destination
        check_recipient_access tcp:127.0.0.1:9926

    virtual_alias_maps = tcp:127.0.0.1:9927   # alias rewriting only

    Port 9926 speaks access(5) semantics (OK / REJECT / DUNNO) and handles
    accept/reject early at SMTP time. Port 9927 handles alias rewriting at
    cleanup time for messages that were accepted.

  --- Questions ---

  1. For Design A: is there any scenario where Postfix would still consult
     virtual_mailbox_maps for something other than smtpd_reject_unlisted_recipient      validation? For example during cleanup, queue re-injection, or virtual(8)
     delivery triggered by some other path?

  2. Are there any other caveats with either design we should be aware of ?

  We are leaning toward Design A as it is the smaller change, but we want to   make sure port 9927 returning NOT_FOUND for every query is genuinely safe and
  not just papering over something Postfix legitimately needs.

  Thanks in advance.
  Rajesh Mishra

On 19/05/26 12:23 pm, Rajesh Mishra wrote:
Hi,

  I want to verify my understanding of the virtual_alias_maps → virtual_mailbox_maps fallback chain before making a configuration change, and also explore whether the chain can be simplified further.

  ---
  Setup:

  virtual_alias_maps   = tcp:127.0.0.1:9926, proxy:mysql:/etc/postfix/mysql-vam.cf
  virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-vmm.cf
  smtpd_reject_unlisted_recipient = yes  (default)

  Port 9926 is an in-house TCP lookup daemon. For each RCPT TO it runs two lookups against our database:

  Step 1 — checks whether the mailbox exists for the recipient address, with domain-alias resolution (if the recipient domain is a configured alias, it resolves to the canonical domain before checking). Returns the address if found. For external domains, returns immediately without proceeding to step 2.

  Step 2 — checks for explicit aliases and catchalls for the recipient address.

  If both steps return nothing, port 9926 returns NOT_FOUND. Postfix then tries the MySQL fallback (mysql-vam.cf), which checks the same alias table as step 2. If that also returns nothing, smtpd_reject_unlisted_recipient triggers virtual_mailbox_maps.

  The virtual_mailbox_maps MySQL lookup is a direct mailbox existence check — same table and same status conditions as step 1 above.

  ---
  Question 1 — Is virtual_mailbox_maps redundant here?

  Since step 1 already checks mailbox existence with identical conditions, by the time virtual_mailbox_maps is reached the same question has already been answered "no" by the same table with the same filters. If the user existed and matched those conditions, step 1 would have returned the address, satisfied virtual_alias_maps, and
  virtual_mailbox_maps would never have been reached.

  We are therefore considering replacing the MySQL virtual_mailbox_maps with a TCP lookup on a separate port that immediately returns NOT_FOUND without hitting the database, on the basis that this check is always a no-op at that point.

  Is our understanding correct? Are there any Postfix-level scenarios — such as lookup caching, address normalisation, or anything in how smtpd_reject_unlisted_recipient interacts with these maps — where virtual_mailbox_maps could legitimately find a recipient that virtual_alias_maps step 1 already missed?

  ---
  Question 2 — Can virtual_mailbox_maps be skipped entirely?

  Taking this further: is there a way to configure Postfix so that NOT_FOUND from virtual_alias_maps is treated as a rejection directly, without consulting virtual_mailbox_maps at all?

  For example, does setting virtual_mailbox_maps = (empty) while keeping smtpd_reject_unlisted_recipient = yes achieve this? Or does Postfix require a non-empty virtual_mailbox_maps to be able to reject unlisted recipients for virtual mailbox domains?


Thanks
Rajesh Mishra

_______________________________________________
Postfix-users mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to