Skip to content

mail-auth-proxy 0.2.1

Choose a tag to compare

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

Added

  • limits.ipv6_source_prefix (default 64, 32-64): the prefix length by which IPv6
    sources are grouped for max_preauth_per_ip and the auth rate limit; 48 treats a
    whole site as one source.
  • Metric mail_auth_proxy_jwks_keys_skipped_total by issuer: JWKS keys skipped because
    a member was missing or undecodable, counted on every fetch. Such a key was visible
    only as a journal warning.

Changed

  • A server.hostname that is a single label, the default mail-auth-proxy included,
    gives a configuration warning: the backend EHLO takes a fully-qualified host name
    (RFC 5321 §4.1.4).
  • oauth.issuers[].client_claim defaults to client_id for token_type = "rfc9068"
    (RFC 9068 §2.2); keycloak and any keep azp. An RFC 9068 issuer with
    allowed_clients that relied on azp must now set client_claim = "azp".
  • Relative file paths in the configuration (tls.cert, tls.key, ca_file,
    domains_file, doveadm_key_file, doveadm_ca_file, users_file) are a validation
    error: they resolved against the working directory, so --check-config in a shell
    could pass on files the service never reads. Use absolute paths.
  • The HELP text of mail_auth_proxy_ratelimit_active_blocks names the update interval:
    every 10 s and when a block starts.

Fixed

  • A token whose JWS header has a crit parameter is refused: the proxy understands no
    JWS extension (RFC 7515 §4.1.11).
  • A token whose iss claim is an array is refused; iss is a string (RFC 7519 §4.1.1).
  • A JWK with missing or undecodable members is skipped with a warning instead of
    discarding the issuer's whole JWKS (RFC 7517 §5).
  • The JWKS of all issuers are fetched concurrently at startup, on refresh and on an
    unknown kid: with several unreachable IdPs a refresh, and every token waiting on
    it, took one 10-second timeout per issuer.
  • The password and rate-limit fingerprint keys are generated at startup: without a
    system RNG the proxy now refuses to start instead of panicking in every session that
    logs a refused login.
  • A JWKS response is used only with a 2xx status; a redirect carrying a JWKS body was
    accepted.
  • mail_auth_proxy_tls_cert_expiry_timestamp_seconds is 0 for a certificate whose
    notAfter has an impossible hour, minute, second or day (such as 31 April) instead of
    a date rolled over into the next day or month.
  • A full legacy throttle table drops the oldest 1024 accounts at once instead of one per
    new failing account, so the table scan under the lock is no longer paid on every new
    account; a failure of an account already tracked evicts nothing.
  • ManageSieve: a backend BYE in answer to AUTHENTICATE (shutdown, connection limit)
    is an outage, not a backend_reject; it no longer counts in the rate limit or for
    CrowdSec (RFC 5804 §1.3).
  • A backend that offers UNAUTHENTICATE (RFC 8437, RFC 5804 §2.14.1) gets no login: an
    outage with a journal line that names it, since a client could otherwise leave its
    login and try passwords past the password gate. ManageSieve no longer relays the
    capability.
  • Token validation (including a JWKS refresh for an unknown kid), the legacy account
    check and the backend login now run inside the pre-auth budget (preauth_secs); a
    slow IdP or a hanging backend no longer holds pre-auth slots beyond it. Running out is
    an outage (retry later), not a failed login.
  • A password mechanism the endpoint does not offer, chosen without an initial response,
    is refused at once (IMAP NO, SMTP 504 5.5.4, ManageSieve NO) instead of prompting
    for a password that would never be used. It is logged as blocked_endpoint without
    pwfp (and with the login only if the client sent it as SASL LOGIN initial response).
  • ManageSieve AUTHENTICATE without initial response reads the client's answer as a
    string (quoted, literal or bare) and takes "*" as a cancel (RFC 5804 §2.1); a
    quoted response was passed to the base64 decoder with its quotes, so such logins
    failed. Client literals must be {n+} with nothing after the header or after the
    octets (RFC 5804 §4); {n}, {n++} and trailing text are refused.
  • ManageSieve: the greeting lists the backend's SIEVE capability from the first
    connection after startup on (RFC 5804 §1.7); while the capability cache is cold the
    greeting waits for the probe. Probes run one at a time (clients that arrive together
    share one), a failed probe counts in mail_auth_proxy_backend_errors_total{proto="sieve"}
    and the next one waits 5 s.
  • SASL parsing is strict where the RFCs are: a NUL in a PLAIN password (RFC 4616 §2), in
    a SASL LOGIN field or in the IMAP LOGIN arguments is refused; the OAUTHBEARER GS2
    header must be n, or y, with an optional a= authzid whose =2C/=3D are decoded
    (RFC 7628 §3.1, RFC 5801 §4), p= channel binding is refused; a XOAUTH2 user= or
    OAUTHBEARER authzid longer than 255 bytes or with control characters is refused. All
    of these end as protocol, before the gate and the backend.
  • SMTP: after a refused AUTH (the proxy takes one per connection) the close is
    announced with 421 4.7.0 <hostname> closing connection after the refusal (RFC 5321
    §3.8).
  • ManageSieve: CAPABILITY and NOOP work before STARTTLS too (RFC 5804 §2), and
    NOOP with an argument answers with the TAG response code (§2.13).
  • ManageSieve backend login: a SASL response longer than 1024 octets (any sizable
    token) is sent as a literal {n+} instead of an over-long quoted string (RFC 5804
    §4).
  • IMAP backend login: the SASL initial response is only sent when the backend's
    greeting advertises SASL-IR; otherwise the response follows the backend's empty
    challenge (RFC 4959 §3).
  • IMAP: the pre-auth capabilities list ID, which the proxy answers (RFC 2971 §3).
  • IMAP and SMTP: an initial response = is the empty response (RFC 4959 §3, RFC 4954
    §4), not undecodable base64. A SASL response that decodes but holds no valid
    credential now gets NO [AUTHENTICATIONFAILED] / 535 5.7.8 instead of BAD /
    501, which stay for responses that are not base64 and for a cancel (RFC 9051
    §6.2.2, RFC 4954 §4).
  • An OAuth response with an empty auth value (auth=, or Bearer without a token) is
    a discovery request (RFC 7628 §4.3): it gets the error result with scope and
    openid-configuration like a rejected token instead of a syntax error. It carries no
    credential, so its authresult line is reason="protocol" (with the mechanism and
    the SASL user), and it counts neither as a failed attempt nor in the rate limit.
  • OAUTHBEARER: a host in the client response must match the TLS server name (SNI),
    ASCII case-insensitive (RFC 7628 §3.2); a mismatch is refused like any rejected token
    (bad_token). Without SNI, and for port, nothing is compared.
  • systemctl reload failed on minimal systems without procps (/bin/kill); the packages
    now depend on it (procps for the deb, util-linux-core for the rpm).
  • An upgrade with a locally changed /etc/mail-auth-proxy/config.toml stopped at dpkg's
    conffile question whenever the shipped example had changed, and unattended-upgrades
    skipped it. The config is no longer a conffile: the package ships the example as
    /usr/share/mail-auth-proxy/config.example.toml, creates config.toml from it on
    install where none exists and never changes an existing one. Upgrading from a release
    that shipped it as conffile keeps the file unchanged, without a question (rpm: no
    .rpmnew; a changed config is still kept as .rpmsave on removal).

Security

  • The packaged unit sets LimitCORE=0: the service writes no core dumps, which could
    hold tokens, passwords and the TLS key. Before, only the soft limit was 0 and the
    hard limit unlimited.

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