mailcheck

git.sig-io.nl · 2026-10-05 · by Long Nguyen ·generic
mailcheck - Check settings for mailservers over the network, with nagios compatibility

mailcheck

Code Issues Pull requests Projects Releases Packages Wiki Activity Actions

Check settings for mailservers over the network, with nagios compatibility

6 commits 1 branch 0 tags 102 KiB

  • Go 99.4%
  • Makefile 0.6%
  • main

    Find a file

    HTTPS

    Download ZIP Download TAR.GZ Download BUNDLE Open with VS Code Open with VSCodium Open with Intellij IDEA

    | Mark Janssen 0d34ea12fb Working: version 5 | 2026-10-05 21:45:16 +02:00 | | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------- | | go.mod | Attempt 2 | 2026-10-05 21:21:18 +02:00 | | go.sum | Working: version 5 | 2026-10-05 21:45:16 +02:00 | | LICENSE | Attempt 2 | 2026-10-05 21:21:18 +02:00 | | main.go | Working: version 5 | 2026-10-05 21:45:16 +02:00 | | Makefile | Initial broken version | 2026-10-05 21:11:00 +02:00 | | README.md | Working: version 5 | 2026-10-05 21:45:16 +02:00 | | smtp\_response\_test.go | Working: version 5 | 2026-10-05 21:45:16 +02:00 |

    mailcheck

    `mailcheck` is a small command-line SMTP/DNS/TLS diagnostic tool written in Go. It checks whether a domain appears correctly configured to receive SMTP mail and can emit Nagios/Icinga-compatible output.

    Checks

  • MX records, sorted by preference
  • implicit-MX fallback to A/AAAA when no MX exists
  • A and AAAA resolution
  • IPv4/IPv6 selection
  • TCP SMTP connectivity
  • SMTP greeting and EHLO
  • STARTTLS support and negotiation
  • optional implicit TLS (`--implicit-tls`, useful for port 465)
  • certificate parsing, hostname/PKIX verification, issuer, dates and expiry
  • TLS 1.2/1.3 and modern AEAD cipher classification
  • TLSA lookup and certificate/public-key matching for TLSA usages 0-3
  • DNSSEC indication using the AD bit returned by the selected recursive resolver
  • SPF record discovery and basic syntax/lookup-limit checks
  • DMARC record parsing and policy checks
  • DKIM selector checks, including RSA and Ed25519 public-key validation
  • human, JSON and Nagios/Icinga output
  • Build

    Requires Go 1.23 or newer.

    go build -o mailcheck .

    For a Linux install:

    install -m 0755 mailcheck /usr/local/bin/mailcheck

    Examples

    ./mailcheck example.com
    ./mailcheck example.com --show-expiry
    ./mailcheck example.com --ipv4
    ./mailcheck example.com --ipv6
    ./mailcheck example.com --host mail.example.net
    ./mailcheck example.com --host mail.example.net --address 192.0.2.25
    ./mailcheck example.com --dkim selector1,selector2
    ./mailcheck example.com --json
    ./mailcheck example.com --nagios
    ./mailcheck example.com --port 465 --implicit-tls
    ./mailcheck example.com --dns 1.1.1.1

    When `--host` is supplied, that hostname is used for TLS SNI/name validation and TLSA lookup. `--address` controls the TCP destination without changing the TLS hostname. If only `--address` is supplied, the domain itself is used as the TLS hostname; for accurate SMTP certificate/DANE testing, supplying the actual MX hostname is preferable.

    Nagios exit status

    0 OK
    1 WARNING
    2 CRITICAL
    3 UNKNOWN

    Example:

    MAILCHECK WARNING - mx.example.net/192.0.2.25 SMTP=OK TLS=OK TLSA=WARNING | duration_ms=142

    DNSSEC / DANE note

    The program requests EDNS DNSSEC data and reports DNSSEC as secure when the recursive resolver returns the DNS AD (Authenticated Data) bit. It does not implement a complete DNSSEC validator itself. For production DANE monitoring, point `--dns` at a validating recursive resolver you trust.

    TLSA matching implements the TLSA selector and matching-type operations and checks usages 0-3 against the certificate chain. The result should be read together with the DNSSEC status: a matching TLSA record obtained without DNSSEC validation is not equivalent to authenticated DANE.

    SPF / DKIM / DMARC scope

    These checks are deliberately DNS-policy diagnostics, not a complete mail-authentication implementation.

    SPF checks the record syntax, multiple-record condition, terminal policy, and the ten-DNS-lookup limit at the top level. It does not simulate every possible sender IP through a complete recursive SPF evaluation.

    DKIM requires selectors because DNS does not provide a universal way to enumerate all selectors. Example:

    ./mailcheck example.com --dkim selector1,google

    The DKIM check validates the public-key encoding and RSA key size / Ed25519 key length, but it does not verify a signed message.

    DMARC validates the record and key policy tags but does not evaluate an actual message's SPF/DKIM alignment.

    Security / operational considerations

    The SMTP test performs a real TCP connection to the target's port 25 (or the explicitly selected port). It does not send mail or authenticate.

    TLS certificate verification is performed separately from the TLS handshake so that a certificate that is not publicly trusted can still be inspected and compared with a DANE TLSA record. This is intentional for SMTP/DANE diagnostics.

    The default TLS client minimum is TLS 1.2. A server that only supports TLS 1.0/1.1 will therefore fail the modern TLS check rather than being silently accepted.

    Suggested future extensions

  • full recursive SPF evaluator with RFC lookup accounting
  • complete DNSSEC chain validation instead of relying on resolver AD
  • more exact RFC 7672 DANE policy evaluation
  • TLS version capability matrix (separate probes for TLS 1.2 and 1.3 and optional legacy probes)
  • SMTP MTA-STS policy retrieval/validation
  • TLS-RPT record checks
  • CAA checks
  • PTR / forward-confirmed reverse DNS diagnostics
  • SMTP banner hostname diagnostics
  • optional concurrency and retry controls
  • DKIM input

    `--dkim` accepts either selectors or complete DKIM owner names. For example:

    mailcheck --dkim 2025a,2025b maniac.nl
    mailcheck --dkim 2025a._domainkey.maniac.nl.,2025b._domainkey.maniac.nl. maniac.nl

    A complete owner name is used as-is; it is not prefixed with the domain a second time.

    DNS implementation

    DNS queries are implemented using the upstream `github.com/miekg/dns` library (v1.1.70), not a hand-written DNS packet encoder/decoder. It provides native RR parsing, UDP/TCP transport, EDNS0 and DNSSEC-related support.

    The tool requests DNSSEC data with the EDNS DO bit and reports the resolver's Authenticated Data (AD) bit separately. An AD=true result means the configured recursive resolver authenticated the response; it does not mean `mailcheck` itself is acting as a validating recursive resolver.

    DKIM selectors

    `--dkim` accepts either selectors:

    mailcheck maniac.nl --dkim 2025a,2025b

    or complete owner names:

    mailcheck maniac.nl --dkim 2025a._domainkey.maniac.nl.,2025b._domainkey.maniac.nl.

    The latter are queried exactly as supplied (apart from normalizing the trailing DNS root dot).

    Command-line option order

    Both forms are accepted:

    mailcheck maniac.nl --json --dkim 2025a
    mailcheck --json --dkim 2025a maniac.nl

    SMTP/TLS diagnostics

    The SMTP probe reports the connection as a sequence of stages: TCP, SMTP greeting, EHLO, STARTTLS, and TLS. A failure at an earlier stage no longer incorrectly marks later stages as failures; later stages are reported as `UNKNOWN` because they could not be tested.

    Use `--debug` to print the SMTP/TLS exchange diagnostics to stderr without putting protocol chatter into JSON or Nagios stdout:

    mailcheck --debug --host mail.example.net example.com

    For example, a TCP timeout is reported as `SMTP=CRITICAL` while TLS and TLSA remain `UNKNOWN`, with the underlying socket error included in the summary.

    TLSA is reported separately as:

  • `WARNING` when TLS succeeds but no TLSA record exists or the TLSA RRset is not DNSSEC-authenticated.
  • `CRITICAL` when a DNSSEC-backed TLSA RRset exists but does not match the negotiated certificate.
  • `UNKNOWN` when TLS was not negotiated, because the certificate cannot be matched to TLSA.

This distinction prevents a failed TCP connection from being reported as a TLS certificate failure.

git.sig-io.nl · 2 images · 40 links

Comments (0)

No comments yet