Coder Social home page Coder Social logo

smtpdane's Introduction

smtpdane

Continuous Integration Coverage Status

SMTP Service monitoring of DANE-protected services, with optional NAGIOS-compatible behavior.

A bit short on tests but has been used for a few years now and so far has both succeeded when it should and failed when it should.


go install go.pennock.tech/smtpdane@latest
(optional helpers documented below) (or use prebuilt binaries)

This is an SMTP client which can connect to an SMTP server, issue STARTTLS 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 DANE-TA(2) and DANE-EE(3); PKIX-TA(0) and PKIX-EE(1) are explicitly unsupported.

This relies upon a validating DNS resolver; we do not yet validate internally. (Most tools should not validate themselves, but perhaps a monitoring tool should?)

Optionally this client can speak TLS-on-connect instead of STARTTLS, for RFC8314 submissions service (historically called smtps or ssmtp); this is port 465 mail service for clients to submit mail.

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.


Below, find:


Installation

Binaries

We use GoReleaser to make binaries automatically, in GitHub CI, when a version tag is pushed.

The builds are reproducible, so that you can verify the builds yourself, if comfortable with such flows.

Find the latest Release on GitHub for pre-built binaries.

We're willing to add builds for other platforms, if there's interest. We don't currently intend to offer OCI images for Container usage, because using DNSSEC with DNS in Containers is less a scenario of "there are corner cases" and more "how did you make something purely out of corners??".

From Source

Minimum Versions

This is free and open source software, offered to the public, and the maintainers can make any changes to build dependencies, at any time, as they deem fit. But we will usually try to be a bit nicer than that.

So we reserve the right to, at any time and without notice, require a minimum version of Go which is in the oldest release series supported by the Go language maintainers. Eg, if 1.21.x is the latest release then 1.20.x will still be supported, so we can add dependencies which require 1.20.x to compile.

Go 1.14 or greater is required; the release of Go 1.15 changed how network errors are returned in some situations; while we didn't happen to hit those, it's now just a matter of time before our situation breaks too, so switched to the errors.As() replacement for interface casting, as introduced in Go 1.13. We use tls.CipherSuiteName() from Go 1.14 for better diagnostics.

( Go 1.8 or greater is required. We use the crypto/tls.Config.VerifyPeerCertificate callback introduced in that release. )

Process

If not cloned as a repo:

$ go install go.pennock.tech/smtpdane@latest

If cloned as a repo, so your shell cwd is inside this repo:

$ go build .

Optionally, use ./.compile instead of go build to embed extra repository information into the binary, but this is less necessary with Go Modules.

With that one go install command, assuming no other Go environment variables set up to move things from defaults, the binary can be found in ~/go/bin/smtpdane. If $GOPATH is set, then look in bin/ inside the first directory in the list given by that variable.

To build as a static binary for deployment into a lib-less environment:

# the current VCS hosting service is subject to change:
git clone https://github.com/PennockTech/smtpdane
cd smtpdane
# simple
./.compile static
# manual:
go build -ldflags "-linkmode external -extldflags -static"

You can instead use GoReleaser:

goreleaser build --snapshot --single-target --clean

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.

Our version numbering is semantic, with the caveat that Go only supports the latest two minor versions of the toolsuite, and PennockTech does not consider it a breaking change to add a dependency upon a stdlib feature which is present in all releases of Go which are currently supported by the Go language maintainers.

Invoking

Invoke with -help to see help output listing known flags and defaults.
See the examples below which make it clear how simple it normally is.

Most commonly: smtpdane -mx my-domain.example.org

The host to connect to is provided as a list of one or more hosts after any options.

Use -port to specify a different port to speak on, for each host which doesn't specify a specific port. Note that -port specifies a default; if looking up SRV records, ports from SRV override the -port option. However, port overrides on the host (see below) override SRV.

Use -tls-on-connect to immediately start TLS instead of negotiating.
Use -mx to indicate that names supplied are domain-names and MX records should be looked up.
Use -submission to do the same but look up service submission SRV records, typically used for port 587 service.
Use -submissions to do the same, looking up for submissions though and forcing on the -tls-on-connect option.

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 [2001:db8::25]:1234.

A SOCKS5 proxy can be used for establishing TCP connections (but not, at this time, for the DNS resolution). Use the -proxy-tcp option to provide a socks5:// URL for establishing the connections.

By default, the EHLO command will supply a hostname of smtpdane.invalid; use the -helo flag to override that value.

Use -quiet (or -q) to not emit any messages unless there's a failure.
Use -terse to shorten the amount of output text.
Use -nagios to use Nagios exit codes (and be -terse & -nocolor).

The -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 monitoring systems.

Examples

# 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

# 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 example.org

# Regular lookup of SMTP Submission for a domain:
smtpdane -submission example.org

# Regular lookup of SMTP Submissions TLS-on-connect for a domain:
smtpdane -submissions example.org

# Connect to port 26 for a server, IPv4-only:
smtpdane -4 -port 26 mx1.example.org

# Check if there is a Submissions (TLS-on-connect, 465) service on
# each IP found for Submission service (587) to confirm that you're
# good to add the newer _submissions._tcp SRV records too:
smtpdane -tls-on-connect -submission example.org:465

# When verifying the certificate, add a different allowed 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

# Establish a SOCKS5 proxy connection in one terminal
ssh -D 5678 external.host.example.net
# Then use that proxy for the TCP connections
smtpdane -proxy-tcp socks5://localhost:5678 -mx example.org

# Resolve using a different set of DNS resolvers;
# one resolver using default port, one with a non-standard port:
DNS_RESOLVER='192.0.2.53, 192.0.2.100:54' smtpdane -mx example.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 NotAfter time. 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 -expiration-warning 1ns 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 -show-cert-info or -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.

A simple invocation for a crontab(5) might be:

17 */3 * * * /home/myname/go/bin/smtpdane -q -expect-ocsp -mx example.org

That will check every 3 hours, at 17 minutes past the hour, and check every IP for every hostname returned by the MX records for the domain, checking certificate validity with default notification periods, and declaring an absence of OCSP information to be an error. No output will be produced as long as everything is fine, but there will be output if there are problems, and cron will send an email.

Access needed

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.

  1. Network connectivity, outbound on port 53, UDP and TCP
    • If /etc/resolv.conf or DNS_RESOLVER specifies another port, then that port too
    • If invoked with a hostname which dispatches to multicast DNS, then likely port 5353
  2. Outbound TCP, on port 25 and any other ports required for monitoring SMTP. (587 and 465 are common choices).
    • Ports can be supplied on the command-line, or via SRV records if invoked with -srv
    • If using -proxy-tcp then access to whichever host/port is specified there.
  3. Stdio, ability to write to stdout/stderr.
  4. /etc/resolv.conf
    • If the DNS_RESOLVER environment variable is set, it's used for resolution, but the libraries still load this file
  5. Read-only access to $SSL_CERT_FILE and $SSL_CERT_DIR locations, and if neither of those is set then to a set of common locations for those files.
    • Inhibit with -nocertnames
  6. Read-only access to /etc/services; on many OSes also /etc/nsswitch.conf to handle indirection to that, and then if that's not just the file, then wherever else services are read from. Sometimes other /etc files used for DNS resolution.
  7. Usually some source of system entropy (/dev/urandom) if not available via a system call.
  8. Any other common OS start-up files used even for statically linked files.
    • Eg, /etc/malloc.conf on some OSes
  9. No other filesystem access should be required, if statically linked.
    • otherwise, everything used by the dynamic loader too

smtpdane's People

Contributors

philpennock avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

alexmtv

smtpdane's Issues

Add an option to disable TSLA check ?

I would like to check SRV records, but in my domain scheme SRV records have different names than MX records.
And the SRV records have no TSLA records. It would be nice to be able to disable TSLA checks for example.

I have TSLA setup only for MX records:

  • mx1.provider.tld
  • mx2.provider.tld

But SRV have values like:

  • imap.provider.tld
  • smtp.
  • pop3.

Test command: smtpdane -show-cert-info -submissions wdes.fr

One last thing, I was thinking -nocertnames would shut it up and check TSLA post connect, but it looks like that's not true 🤔

DNS resolver iteration is always using all resolvers

While the issue in #4 was accurate, it shouldn't have been seen because the DNS resolver shouldn't have been used. So a real issue was reported, but it is a bug that it was found to be reported.

Adding a debug to the resolver iteration inner loop, just before the exchange, shows we hit all the resolvers. This should not be.

`crypto/x509/pkix.Name` now implements `String() string`, use it

In the code, wherever we currently reference .CommonName, that was a short-cut because I didn't want to write a full name construction function.

As of Go 1.10, per release notes:

crypto/x509/pkix
Name now implements a String method that formats the X.509 distinguished name in the standard RFC 2253 format.

We should switch to using this and make sure the results still look sane.

Incompatible or non friendly behavior of -srv and friends

$ smtpdane -show-cert-info -srv -submissions wdes.fr
error resolving SRV "_-submissions._tcp.wdes.fr":
	no results found

The generated record makes no sense, I was thinking that submissions and friends would be a sub category
But it turns out that I need to remove -srv to use -submissions or it will create a very wrong record.

I did end up into this because I first tried -srv and then realised it needed one more, so I just added -submissions and a 🐛 was born

Other than that, thank you so much for considering SRV !

Fails with `not AD set for results from 141.14.16.1:53 for "mx3.molgen.mpg.de."/A query`

$ git log --oneline --no-decorate -1
1632cba CI: adjust for GHActions deprecations [tread-water]
$ go build
$ ./smtpdane mx3.molgen.mpg.de
error securely resolving "mx3.molgen.mpg.de"
    not AD set for results from 141.14.16.1:53 for "mx3.molgen.mpg.de."/AAAA query
    not AD set for results from 141.14.16.1:53 for "mx3.molgen.mpg.de."/A query
    no results found

./smtpdane: encountered 1 errors

$ more /etc/resolv.conf
search molgen.mpg.de
nameserver 127.0.0.1
nameserver 141.14.16.1
$ dig mx3.molgen.mpg.de

; <<>> DiG 9.9.6-P1 <<>> mx3.molgen.mpg.de
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47299
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;mx3.molgen.mpg.de.         IN      A

;; ANSWER SECTION:
mx3.molgen.mpg.de.  7129    IN      A       141.14.17.11

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Jul 13 05:45:40 CEST 2023
;; MSG SIZE  rcvd: 62

$ unbound -V
Version 1.13.1

Configure line: --prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/libexec --sysconfdir=/etc --sharedstatedir=/var --localstatedir=/var --libdir=/usr/lib --includedir=/usr/include --datarootdir=/usr/share --datadir=/usr/share --infodir=/usr/share/info --localedir=/usr/share/locale --mandir=/usr/share/man --docdir=/usr/share/doc/unbound --exec-prefix=/usr --disable-static --enable-systemd --with-pidfile=/run/unbound.pid --with-rootkey-file=/var/lib/unbound/root.key
Linked libs: mini-event internal (it uses select), OpenSSL 1.1.1t  7 Feb 2023
Linked modules: dns64 respip validator iterator

BSD licensed, see LICENSE in source package for details.
Report bugs to [email protected] or https://github.com/NLnetLabs/unbound/issues

Mis-parsing OCSP status

The Golang OCSP lib uses ResponseError in constructed responses and the OCSP status parsing we're doing is assuming those codes (.TryLater etc) instead of the in-response status values (.Revoked etc).

Use an alternative DNS server

Hi,

It would be awesome to be able to use a defined DNS name server.

Because for my specific uses my servers can query smtp.domain.tld internally and it returns an internal IP.
But from the outside world it gives the public IP.

Either allow unsecure DNS results, but thats a bit sad:

error securely resolving "mx1.mails.domain.tld"
	not AD set for results from 10.10.18.1:53 for "mx1.mails.domain.tld."/AAAA query, skipping any remaining resolvers
	not AD set for results from 10.10.18.1:53 for "mx1.mails.domain.tld."/A query, skipping any remaining resolvers
	no results found

Or support using another resolver. That could allow the user to do checks with different resolvers to monitor if results do not differ

PS: this project is awesome, and having .deb releases is great !

Using non-TLS-on-connect against TLS-on-connect port should error cleanly, or handle

At present, using smtpdane -srv submission $DOMAIN without also specifying -tls-on-connect will result in a hang.
(The -submissions shortcut implicitly sets the -tls-on-connect flag.)

We should handle this more gracefully. Hanging forever is not acceptable.

It is acceptable to spot the TLS-on-connect and issue a warning and continue.

It is acceptable to spot the TLS-on-connect and error.

It is acceptable to just have a timeout over the whole handshake and abort when that is exceeded.

At present, we do have the -connect-timeout flag, but that doesn't apply once the connection has been established.

Spotted while investigating #7

MX domain is added to list of hostnames

Hi!
If I call smtpdane with -mx option it seems to add the domain as hostname as well into the list of hostnames to check.

For example:

# ./smtpdane -mx univie.ac.at
found 4 MX records for "univie.ac.at" across 1 preference levels
  "univie.ac.at" MX preference 10: [zidmx4.univie.ac.at. zidmx3.univie.ac.at. zidmx2.univie.ac.at. zidmx1.univie.ac.at.]
found 2 secure addresses for "zidmx1.univie.ac.at.": [2001:62a:4:25::25:100 131.130.3.100]
found 2 TLSA records for "_25._tcp.zidmx1.univie.ac.at."
  3 1 1 c0578936f55c1800aa6ac8f74116da06c0f8910f8081732cae8f0967ef320425
  2 1 1 f3ae75c0490c907e5fb6268ba79ee8aa6c772874c5cc3829ed97895d1d13a01b
[zidmx1.univie.ac.at. 131.130.3.100] issuing STARTTLS [port 25]
[zidmx1.univie.ac.at. 131.130.3.100] TLSA DANE-EE(3) match: 3 1 1 ...ae8f0967ef320425
[zidmx1.univie.ac.at. 131.130.3.100] 1 chains to TA; first length 2, is: ["zidmx1.univie.ac.at" "TERENA SSL CA 3"]
[zidmx1.univie.ac.at. 131.130.3.100] no valid TA chains for hostname "univie.ac.at"
[zidmx1.univie.ac.at. 131.130.3.100] TLSA DANE-TA(2) match against chain position 2: 2 1 1 ...ed97895d1d13a01b
....

This results in

...no valid TA chains for hostname "univie.ac.at"

messages. Should "univie.ac.at" be tested in this setting? IMO no, or do I miss something?
The same happens with "-submission(s)" and "-srv".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.