SMTP DANE testing tool
EARLY ALPHA SOFTWARE
THIS HAS NOT YET BEEN TESTED TO CONFIRM IT FAILS WHEN IT SHOULD, AGAINST BAD CERTIFICATES OR DNS
Go 1.8+ :
go get go.pennock.tech/smtpdane (but see helpers below)
This is an SMTP client which can connect to an SMTP server, issue
and verify the certificate using DANE (TLSA records signed with DNSSEC).
Validity of the certificate is checked, including date validity periods, but
not PKIX CA anchoring.
Per RFC7672 we only support
PKIX-EE(1) are explicitly unsupported.
To avoid requiring a local DNSSEC validating DNS resolver, DNSSEC is validated
inside the [NB: this functionality not yet implemented,
still rely upon a validating resolver right now.]
smtpdane tool. To achieve this, the root zone trust anchors are
baked into the code. Every few years these are rotated and
need to be updated and rebuilt.
Optionally this client can speak TLS-on-connect instead of STARTTLS, to aid
smtps (commonly deployed on the non-IANA-assigned port 465).
The tool will connect to each SMTP server specified, in parallel. If there are multiple IP addresses, then each will be connected to, in parallel.
Flags may be used to request looking up MX records or SRV records for a domain.
You should be able to write a security sandbox profile to constrain this tool, based upon the information here. If it’s not listed but is needed, then that’s a documentation bug, please report it.
DNS_RESOLVERspecifies another port, then that port too
DNS_RESOLVERenvironment variable is set, it’s used for resolution, but the libraries still load this file
$SSL_CERT_DIRlocations, and if neither of those is set then to a set of common locations for those files.
/etc/services; on many OSes also
/etc/nsswitch.confto handle indirection to that, and then if that’s not just the file, then wherever else services are read from. Sometimes other
/etcfiles used for DNS resolution.
/dev/urandom) if not available via a system call.
/etc/malloc.confon some OSes
Go 1.8 or greater is required. We use the
crypto/tls.Config.VerifyPeerCertificate callback introduced in that release.
$ mkdir ~/go $ go get go.pennock.tech/smtpdane
With those install steps, the binary can be found in
go get command will fetch this repo, any dependent repos and perform the
build. This assumes that
$GOPATH and other Golang-controlling environment
variables have not been set; as of Go 1.8,
~/go is the default solitary
entry in the
~/go/src/go.pennock.tech/smtpdane/.compile to build with embedded
version information from various repositories.
To build as a static binary for deployment into a lib-less environment:
# simple ~/go/src/go.pennock.tech/smtpdane/.compile static # manual: cd /go/src/go.pennock.tech/smtpdane go build -ldflags "-linkmode external -extldflags -static"
At this time there is no vendoring of dependencies. If this matters in your environment, capture them for your use-cases. If our dependency list grows to include packages with unstable APIs then this decision will be revisited.
./.compile instead of
go build to embed extra repository
information into the binary.
-help to see help output listing known flags and defaults.
The host to connect to is provided as a list of one or more hosts after any options.
-port to specify a different port to speak on, for each host which
doesn’t specify a specific port.
-port specifies a default; if looking up SRV records, ports from
SRV override the
-port option. However, port overrides on the host override
-tls-on-connect to immediately start TLS instead of negotiating.
The port can be included with the host in the usual
:1234 suffix notation;
if the host is an IPv6 address, either do not include a port or use the
otherwise-optional square-brackets, thus
By default, the
EHLO command will supply a hostname of
-helo flag to override that value.
-q) to not emit any messages unless there’s a failure.
-terse to shorten the amount of output text.
-nagios to use Nagios exit codes (and be
-quiet approach is suitable for cron jobs which should only emit when
there’s a problem. The
-nagios approach is better for less ad-hoc
monitoring. We’re open to supporting other output formats for other
# Regular lookup of a host; check every address-record: smtpdane mx1.example.org # Regular lookup of a domain; check every MX, every address: smtpdane -mx example.org # Regular lookup of SMTP Submission for a domain: smtpdane -submission example.org # Connect to port 26 for a server, IPv4-only: smtpdane -4 -port 26 mx1.example.org # Check TLS-on-connect for a server: smtpdane -port 465 -tls-on-connect smtp.example.org # Connect to the "usual" de-facto standard TLS-on-connect SMTPS port, # on each host which is a submission host for the domain, # and speak TLS-on-connect: smtpdane -tls-on-connect -submission example.org:465 # Also try checking another hostname smtpdane -aka mail.example.net mail.example.org # See much more information about the certs smtpdane -show-cert-info -mx example.org # See expiring certificates much sooner; alas, Golang duration parsing # maxes out in units of hours, so extend in shell; # 3 months of 31 days each, 24 hours per day, don't forget 'h' unit smtpdane -expiration-warning $((3*31*24))h -mx example.org # Turn missing OCSP stapling information into an error smtpdane -expect-ocsp -mx example.org # Be invoked for Nagios monitoring, with terse output, no color codes, # avoiding stderr, but checking for OCSP (& DANE) on all MX servers smtpdane -nagios -expect-ocsp -mx spodhuis.org
Note that the
-aka names are added to the list of “acceptable” names; you’ll
see each success/failure if you pay attention to the output, but as long as
one name succeeds, the probe of that
host:ip will be deemed a success.
The expiration time of all certificates in the validated chain is checked
for validity, unless
-expiration-warning 0s is passed.
This examines the
NotBefore is ignored.
Only the validated chains are examined, so multiple-chain presentations
require more care to check each thoroughly (suggestions welcome).
While a normal TLS client only checks the current time, smtpdane checks two
times: it checks for outright expired certificates, treating those as errors,
and it checks for “expiring soon” certificates, treating those as warnings.
To effectively only check for outright expiry, use
to shift the warning to be enabled with a 1 nanosecond warning period; this
leaves warnings as technically possible, albeit somewhat unlikely.
OCSP status is only reported if either
-expect-ocsp is passed. The latter will cause missing OCSP information to
be treated as an error, and present/good OCSP information to be shown in
green. Note that a
TryLater response-code is treated as a warning.