Skip to content

mail-auth-proxy 0.3.0

Choose a tag to compare

@NK-IT-CLOUD NK-IT-CLOUD released this 29 Sep 12:59
· 32 commits to main since this release
Immutable release. Only release title and notes can be modified.

Added

  • SIGHUP (systemctl reload) reloads the configuration file without closing any
    connection. The file passes the checks of --check-config (warnings logged) and is
    taken over as a whole: legacy rules, [legacy], users and domains files, issuers
    (a new issuer's JWKS is fetched by the reload), [limits], [auth_ratelimit],
    [timeouts], [session], [scope], server.hostname, the certificates in [tls]
    and [[tls.certificates]], the backends and the submission and sieve settings.
    Connections accepted afterwards use the new configuration; open ones keep the one they
    were accepted with until they end. Rate-limit counts and blocks, the legacy throttle,
    the backends' cached capabilities and the keys of issuers that stay are carried over.
    A change of a listener address, a listener added or removed, or of the metrics
    endpoint needs a restart: such a file, and one that is invalid, is refused as a whole
    with one ERROR reload: configuration not loaded … line, and the configuration in use
    stays. Package upgrades still restart the service (a new binary).
  • mail_auth_proxy_config_reload_total{result="ok|error"} and
    mail_auth_proxy_config_last_reload_success_timestamp_seconds (set at startup and by
    each successful reload).
  • IMAP LOGIN takes its user name and password as literals (RFC 9051 §6.2.3, §9): a
    synchronising {n} after a + continuation, a non-synchronising {n+} at once, up
    to 16384 octets each. A password a client sends as a literal (for example one with
    8-bit characters) was refused with BAD. Where the connection offers no LOGIN, a
    synchronising literal is refused with NO before the continuation.
  • XCLIENT to the submission backend carries the client's post-TLS EHLO or HELO name
    (HELO=), PROTO=ESMTP or SMTP and the client's source port (PORT=), each where
    the backend lists it, so its Received: header names the client's greeting instead
    of the proxy's (RFC 5321 §4.4). smtpd_helo_restrictions on the submission service
    now apply to the client's name.
  • oauth.issuers[].identity_domains: the domains an issuer may log in to. A token whose
    identity is not an address in one of them is a bad_token. With more than one issuer,
    each issuer without it gives a configuration warning: every issuer could otherwise log
    in to every other issuer's mailboxes (OIDC Core §5.7).
  • ALPN on the IMAP and ManageSieve listeners (imap, managesieve, RFC 7301): a client
    that offers ALPN without the listener's identifier is refused in the TLS handshake, so
    a TLS session meant for another service cannot be redirected to them (RFC 9325 §3.8,
    ALPACA). Clients without ALPN are unaffected; SMTP has no identifier and ignores it.
  • Certificates by SNI (RFC 6066 §3): [[tls.certificates]] (cert, key) next to the
    [tls] default. A client gets the certificate that carries the name it asks for, an
    exact subjectAltName DNS name first, then a wildcard for one leftmost label (RFC 9525
    §6.3); a client without SNI gets tls.cert. SIGHUP reloads each certificate on its
    own; --check-config loads every pair and warns about a legacy.rules[].sni name no
    certificate carries. The [tls] cert/key form is unchanged.

Changed

  • SIGHUP re-reads the configuration file as well as the certificates and the JWKS.
    After a refused reload each certificate is still re-read on its own as before, so a
    renewal works while the configuration file is broken. The series of
    mail_auth_proxy_jwks_* and mail_auth_proxy_tls_cert_expiry_timestamp_seconds
    follow the issuers and certificate files of the configuration in use.
  • A source added to auth_ratelimit.exempt_networks (or covered by exempt_internal)
    is no longer blocked, also when its block started before.
  • A client that asks (SNI) for a name that no configured certificate carries is refused
    in the TLS handshake with the fatal alert unrecognized_name (RFC 9325 §3.7); it got
    the one certificate before. The accepted names are the subjectAltName DNS names of the
    certificates. Clients without SNI are unaffected.
  • mail_auth_proxy_tls_cert_expiry_timestamp_seconds has the label cert (the
    certificate file), one series per certificate. With one certificate there is still one
    series, now labelled; a query or an exact series match on the unlabelled series must
    be adjusted, and with several certificates an alert takes the earliest (min).
  • The EHLO reply after STARTTLS lists the submission backend's own extensions instead of
    a static list (RFC 5321 §4.2.4), as far as the proxy handles each one: PIPELINING,
    SIZE (with the backend's limit, advertised now), 8BITMIME, SMTPUTF8, DSN,
    ENHANCEDSTATUSCODES and CHUNKING; never XCLIENT, XFORWARD, VRFY, ETRN or unknown
    ones. The proxy reads them with a probe connection of its own, at startup and then at
    most once per submission.capability_cache_secs (new, default 600), one at a time;
    failed probes count in backend_errors_total{proto="smtp"}, and the last list stays
    in use. submission.ehlo_extensions is now optional and narrows the list by keyword:
    existing lists keep working, their parameters are ignored and keywords the proxy
    never passes on give a warning.
  • BDAT before AUTH is answered with 530, then 421 and the close: its chunk was
    read as commands (RFC 3030 §2).
  • A connection takes up to limits.max_auth_attempts authentication attempts (new,
    default 3, 1-10) instead of one (RFC 9051 §6.2.2, RFC 4954 §4, RFC 5804 §2.1), so
    clients that fall back from one mechanism to another on the same connection (Python
    smtplib from PLAIN to LOGIN, Thunderbird) or retry with a refreshed token no longer
    lose it. Each attempt gets its own authresult line (format unchanged), counts for
    the rate limit and passes the legacy gate and the refusal timing like one on a new
    connection; the pre-auth budget and max_preauth_commands span all of them. A source
    the rate limit blocks while the connection is open is closed at its next credential,
    before it is judged. After an outage, the last attempt, an unknown command or a
    malformed one, the connection closes as before. Set 1 for the old behaviour.
  • The ManageSieve backend's capabilities are probed once at startup, so the first
    greetings no longer wait for a probe. A failure there is logged and counted in
    mail_auth_proxy_backend_errors_total{proto="sieve"}; the start goes on.
  • IMAP advertises IMAP4rev1 without IMAP4rev2 before login. The proxy does not know
    whether the backend speaks IMAP4rev2; after login the backend's own list is relayed as
    before, with IMAP4rev2 where the backend offers it. Dovecot enables IMAP4rev2 only
    with imap4rev2_enable.

Fixed

  • Client lines are read without arming the idle timer for every byte that has already
    arrived. A long line (a 10 KB password) took several times longer to read than to
    receive, and under CPU load its refusal came measurably later than that of a short
    wrong password.
  • SMTP replies before authentication: before TLS every command other than NOOP, EHLO,
    STARTTLS and QUIT gets 530 5.7.0 (RFC 3207 §4; HELO and RSET got 250), STARTTLS
    with parameters 501 5.5.4; after TLS and before AUTH 530 5.7.0 for every command
    other than AUTH, EHLO, HELO, NOOP, RSET and QUIT (RFC 4954 §6) and 503 5.5.1 for
    STARTTLS; an unrecognised command 500 5.5.1 instead of 502 (RFC 5321 §4.2.4);
    250 2.0.0 OK with an enhanced status code for NOOP and RSET (RFC 2034 §4).
  • A JWK whose key_ops does not include verify is skipped like one with a use other
    than sig (RFC 7517 §4.3).
  • The PROXY v2 header carries IPv4-mapped IPv6 addresses (a dual-stack listener) as
    TCP over IPv4, so the backend logs 192.0.2.7, not ::ffff:192.0.2.7.
  • ManageSieve answers the pre-TLS command limit with BYE instead of NO before it
    closes the connection (RFC 5804 §1.2), as after TLS.
  • A client and local address of different families, which cannot be put in one PROXY
    header, fail the backend connection as an outage. The connection went ahead without
    the header, which the backend refuses or logs with the proxy's address.
  • A TLS session the proxy ends before the relay (a refused credential, an outage, a
    pre-authentication timeout, LOGOUT or QUIT) ends with a TLS close_notify (RFC 8314
    §3.4). The connection was dropped without one, which clients report as a truncated
    session.

Install

Debian and Ubuntu: APT repository, see INSTALL.md.
Other systems: download the package or archive and SHA256SUMS, SHA256SUMS.asc and
release-signing-key.asc, then:

gpg --import release-signing-key.asc   # 0B58 3E99 144D CD9B 5A34  D96B E52C 6D1E FB67 927B
gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS