72 Commits

Author SHA1 Message Date
Alberto Bertogli
f7e0e9fe65 Minor cleanups for staticcheck issues
staticcheck found a couple of minor code cleanup improvements, like
unused variables or an out-of-order defer, mostly in tests.

This patch fixes those problems by making the necessary adjustments.

They're all fairly small, and should not change the logic in any
significant way.
2025-10-24 12:15:36 +01:00
Alberto Bertogli
7a4a4e4b34 tests: Fix "text file busy" race condition in go-build-cached
There are a couple of places in the tests when we attempt to build and
run simultaneously. Here, if the build is slow, there is a race where
"text file busy" can appear.

To fix this, build to a temporary file with a random name, then
atomically rename it to the final binary name.
2025-10-18 16:26:51 +01:00
Alberto Bertogli
9999a69086 aliases: Implement "via" aliases
This patch implements "via" aliases, which let us explicitly select a
server to use for delivery.

This feature is useful in different scenarios, such as a secondary MX
server that forwards all incoming email to a primary.

For now, it is experimental and the syntax and semantics are subject to
change.
2025-04-12 23:23:21 +01:00
Alberto Bertogli
1cf24ba94a Auto-format the Python scripts
We have a few Python scripts which over the years ended up with a
variety of formatting.

This patch auto-formats them using `black -l 79` to make them more
uniform, and easier to read and write.
2025-04-11 14:17:17 +01:00
Alberto Bertogli
b65ec36916 test/util/minidns: Support priority in MX records
minidns supports MX records, but today it hard-codes priority=10.

This is limiting when creating test scenarios that depend on having
different MX priorities.

This patch adds support for specifying the priority in MX records.
2025-04-11 14:17:17 +01:00
Alberto Bertogli
723c47d352 test: Update deprecated ssl.wrap_socket() call
ssl.wrap_socket() has been deprecated and is no longer functional in
Python 3.12: https://docs.python.org/3/whatsnew/3.12.html#ssl.

This patch replaces it with the equivalent (in this context)
ssl.SSLContext.
2024-10-31 13:10:14 +00:00
Alberto Bertogli
7a2e49c849 test/t-21-dkim: Add cross-tool check against driusan/dkimverify
This patch adds a cross-tool integration check that uses
driusan/dkim's dkimverify to confirm it can verify our own DKIM signatures.

It is optional, since the tool may not be present.
2024-03-12 20:43:21 +00:00
Alberto Bertogli
5eded4edc3 test: Unify (most) SMTP client calls
To send mails, today some tests use msmtp and others our internal smtpc.py.

This works, but msmtp slows down the tests significantly, and smtpc.py
is also not particularly fast, and also has some limitations.

This patch introduces a new SMTP client tool written in Go, and makes
almost all the tests use it.

Some tests still remain on msmtp, mainly for client-check compatibility.
It's likely that this will be moved in later patches to a separate
special-purpose test.

With this patch, integration tests take ~20% less time than before.
2024-03-12 20:43:21 +00:00
Alberto Bertogli
3c3c32e2fb test: Don't run "go build" each time a helper is invoked
Our tests invoke a variety of helpers, some of them are written in Go.
Today, we call "go build" (directly or indirectly via "go run"), which is
a bit wasteful and slows down the tests.

This patch makes the tests only build our Go helpers once every 10s at
most.

The solution is a bit hacky but in the context of these tests, it's
practical.
2024-03-12 20:43:21 +00:00
Alberto Bertogli
76a72367ae dkim: Implement internal dkim signing and verification
This patch implements internal DKIM signing and verification.
2024-03-12 20:43:21 +00:00
Alberto Bertogli
1208ea1082 test: Fix generate_cert cache age logic
The generate_cert cache has a bug because it uses the directory's age,
which won't necessarily change, and it was always re-generating
certificates after 10m.

This patch fixes the bug by checking the age of the private key file
instead of the directory.
2024-03-09 13:04:25 +00:00
Alberto Bertogli
96fe38c479 coverhtml: Add dark mode support
This patch adds a dark mode support to coverhtml's output.
2024-03-07 23:07:37 +00:00
Alberto Bertogli
a996106eee smtpsrv: Strict CRLF enforcement in DATA contents
The RFCs are very clear that in DATA contents:

> CR and LF MUST only occur together as CRLF; they MUST NOT appear
> independently in the body.

https://www.rfc-editor.org/rfc/rfc5322#section-2.3
https://www.rfc-editor.org/rfc/rfc5321#section-2.3.8

Allowing "independent" CR and LF can cause a number of problems.

In particular, there is a new "SMTP smuggling attack" published recently
that involves the server incorrectly parsing the end of DATA marker
`\r\n.\r\n`, which an attacker can exploit to impersonate a server when
email is transmitted server-to-server.

https://www.postfix.org/smtp-smuggling.html
https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-e-mails-worldwide/

Currently, chasquid is vulnerable to this attack, because Go's standard
libraries net/textproto and net/mail do not enforce CRLF strictly.

This patch fixes the problem by introducing a new "dot reader" function
that strictly enforces CRLF when reading dot-terminated data, used in
the DATA input processing.

When an invalid newline terminator is found, the connection is aborted
immediately because we cannot safely recover from that state.

We still keep the internal representation as LF-terminated for
convenience and simplicity.

However, the MDA courier is changed to pass CRLF-terminated lines, since
that is an external program which could be strict when receiving email
messages.

See https://github.com/albertito/chasquid/issues/47 for more details and
discussion.
2023-12-24 10:43:27 +00:00
Alberto Bertogli
e03594a2c7 test: Make mail_diff more strict
This patch makes mail_diff more strict in its parsing, to ensure we
catch any encoding issues that may otherwise be masked by the default
compatibility policy.
2023-12-23 14:10:16 +00:00
Alberto Bertogli
e6c6df457d chasquid-util: Use server for aliases-resolve and domaininfo-remove
This patch makes chasquid-util's aliases-resolve and domaininfo-remove
commands talk to the chasquid server (via the new localrpc server).

For aliases-resolve, currently has fairly hacky logic which reimplements
a bunch of the servers', and is also incomplete because it does not
support hooks.

In this patch we fix that by having it talk to the server, where we get
authoritative responses and have no issues with aliases hooks. This
resolves https://github.com/albertito/chasquid/issues/18.

For domaininfo-remove, currently its implementation is also very hacky
since it manipulates files behind the servers' back and without even
using the internal library.

In this patch we fix that by doing the operation through the server,
avoiding the need for those hacks, and also remove the need to manually
reload the server afterwards.
2023-07-30 13:21:07 +01:00
Alberto Bertogli
7d6a59ba77 test: Update coverage tests to Go 1.20
Go 1.20 finally includes proper support for instrumenting binaries for
coverage. This allows us to drop quite a few hacks and workarounds that
we used for it, and we can now also test exiting cases.

The downside is that coverage tests now require Go 1.20, but it is an
acceptable price to pay for the more accurate results.

Normal integration tests are unchanged.

This patch updates the coverage testing infrastructure to make use of
the new Go 1.20 features.
2023-02-01 23:43:12 +00:00
Alberto Bertogli
948cee1ce1 Improve bash quoting, and other similar best practices
This patch updates the shell scripts with some of the common best
practices, which should make them more resilient to unusual failures and
unexpected environments (in particular, directories with spaces).

Most of these were identified by shellcheck.
2022-11-13 11:09:19 +00:00
Alberto Bertogli
4d1526e0c8 test: Reduce main sources of overhead in integration tests
The integration tests spend a lot of time on some ancilliary actions,
which slows them down: generating certificates, adding users, and
waiting for things to happen.

To improve the performance of those actions, this patch:

- Makes (most) tests use plain passwords (-20%)
- Adds a certificate cache to reuse certs (-10%)
- Tightens the sleep loops (-5%)

In aggregate, this patch results in a speedup of the integration tests
of ~30-40%.

Note that some of the tests required adjusting the username, because
`chasquid-util user-add` would convert them to lowercase as per PRECIS
mapping.
2022-11-13 11:09:19 +00:00
Alberto Bertogli
4a00a83c23 Add tracing annotations
This patch changes several internal packages to receive and pass tracing
annotations, making use of the new tracing library, so we can have
better debugging information.
2022-11-13 11:09:19 +00:00
Alberto Bertogli
3ebe5c5173 Replace uses of ioutil
ioutil package was deprecated in Go 1.16, replace all uses with their
respective replacements.

This patch was generated with a combination of `gofmt -r`, `eg`, and
manually (for `ioutil.ReadDir`).
2022-11-12 20:06:35 +00:00
Alberto Bertogli
776bdc58ab Update Go doc comments to Go 1.19's format
This patch is the result of running Go 1.19's `gofmt` on the codebase,
which automatically updates all Go doc comments to the new format.

https://tip.golang.org/doc/go1.19#go-doc
2022-09-02 11:11:40 +01:00
Alberto Bertogli
e85c31782b Fix misc. linter issues (comments, variable naming, etc.)
We've accumulated a few linter issues around comments and a couple of
variable names.

While none of them is major, this patch cleans them up so it's easier to
go through the linter output, and we can start being more strict about
it.
2022-08-27 23:49:33 +01:00
Alberto Bertogli
3eed7cd1a9 test: Use our own generate_cert helper
The current generate_cert helper was originally taken from Go's source,
and is more complex than we need it to be.

This patch replaces it with our own version, rewritten from scratch
independently.
2022-08-27 23:39:40 +01:00
Alberto Bertogli
21e8d50df6 test: Improve layout of helper binaries
This patch moves the test helper binaries to a "one directory per
helper" layout, and also makes them to be ignored in the coverage build
instead of all builds.

With this change, "go build ./..." will build all binaries including the
test helpers, which helps make sure that module manage automation also
considers them. In particular, this makes "go mod tidy" work fine.
2022-08-27 18:46:54 +01:00
Alberto Bertogli
5bb17c7066 Update build tag constraints
This patch updates all build tag constraints to add the new format,
alongside the old one, to maintain backwards compatibility.

This was done by using `go fmt`.

See https://go.dev/doc/go1.17#gofmt and
https://golang.org/design/draft-gobuild for more details.
2022-08-08 17:52:34 +01:00
Alberto Bertogli
14e270b7f5 test: Replace uses of which with command -v
The `which` command isn't guaranteed to be available, it is just
extremely common; `command -v` is the standard way to do find an
executable program. See https://lwn.net/Articles/874049/ for more
details.

This patch replaces the uses of `which` with `command -v`, which only
appears in a couple of tests.
2021-10-29 11:14:16 +01:00
Alberto Bertogli
ed38945fca courier: Use DNSError.IsNotFound to identify NXDOMAIN
When resolving MX records, we need to distinguish between "no such
domain" and other kinds of errors. Before Go 1.13, this was not
possible, so we had a workaround that assumed any permanent error was a
"no such domain", which is not great, but functional.

Now that our minimum supported version is Go 1.15, we can remove the
workaround.

This patch replaces the workaround with proper logic using
DNSError.IsNotFound to identify NXDOMAIN results when resolving MX
records.

This requires to adjust a few tests, that used to work on environments
where resolving unknown domains (used for testing) returned a permanent
error, and now they no longer do so. Instead of relying on this
environmental property, we make the affected tests use our own DNS
server, which should make them more hermetic and reproducible.
2021-10-08 23:11:29 +01:00
Alberto Bertogli
d78056aff5 test: Skip integration tests if $HOSTALIASES is not functional
Most integration tests depend on the $HOSTALIASES environment variable
being functional. That variable works on most systems, but not all. In
particular, systems with `systemd-resolved` can cause the variable to be
ignored.

This was reported by Alex Ellwein in
https://github.com/albertito/chasquid/issues/20.

This patch makes the affected tests to be skipped if $HOSTALIASES is not
working properly. It also removes unnecessary hosts files from tests
which don't need it, and documents this behaviour.

Thanks to Alex Ellwein and foxcpp@ for reporting and helping investigate
this issue!
2021-07-15 00:20:21 +01:00
Alberto Bertogli
5c09138db8 chasquid-util: Remove dependency on docopt-go
The docopt-go library is quite convenient, but it has been abandoned for
a while :(

Since we only use it for chasquid-util, this patch removes it and
replaces it with a custom small parser, that is a reasonable fit for the
required use cases.

The patch also adds a couple of tests to increase coverage.

NOTE: docopt-go accepted some undocumented behaviour, in particular the
use of "-a b" instead of "-a=b". The new parser does not, so some
user scripts may require updating.

I think this should be rare enough not to be worth the complexity of
adjusting the parser to allow it.
2021-01-16 23:21:35 +00:00
Alberto Bertogli
aa9455c418 test: Ignore fexp in the regular Go build
fexp is a testing utility, including it in the regular Go build confuses
some automation as it can think it's part of chasquid proper.

All other testing utilities are ignored via the "+build ignore"
annotation for this reason, so this patch adds it to fexp to fix this
issue.
2020-11-22 11:57:14 +00:00
Alberto Bertogli
e79586a014 Implement HAProxy protocol support
This patch implements support for incoming connections wrapped in the
HAProxy protocol v1.

This is useful when running chasquid behind a HAProxy server, as it
needs the original source IP to perform SPF checks.

This patch is a reimplementation of one originally provided by Denys
Vitali in pull request #15, except the logic for the protocol handling
is moved to a new package, and the smtpsrv.Conn handling of the source
IP is simplified.

It is marked as experimental for now, since we want to give it a bit
more exposure just in case the option/api needs adjustment.

Thanks a lot to Denys Vitali (@denysvitali in github) for sending the
original patch for this, and helping test it!
2020-11-13 20:49:42 +00:00
ThinkChaos
bb1b921e3c Add /exit endpoint to monitoring server
Allows terminating chasquid via the network. Useful to trigger a restart
(if there is an init system to relaunch chasquid) and thus reload certificates.

Amended-by: Alberto Bertogli <albertito@blitiri.com.ar>
  Added tests, and adjusted shutdown sequence.
2020-11-12 23:24:21 +00:00
Alberto Bertogli
e9c6775418 test: Remove dependency on wget
This patch removes the dependency on wget for fetching content over
http, which was used in one of the tests to do some checking on debug
and metric pages, as well as loop detection.

Instead of wget, we now use a small built-in utility called fexp.
2020-11-12 23:24:21 +00:00
Alberto Bertogli
4357379737 test: Implement retries on the load generator
If the load generator is sending emails too fast, chasquid queue might
hit the maximum size and fail the test.

This patch makes it sleep and retry, to give the server some time to
catch up.

Thanks to Max Mazurov (fox.cpp@disroot.org) for reporting this problem.
2020-03-28 16:42:04 +00:00
Alberto Bertogli
150976b905 test: Use a pretty table for the coverage html
This patch improves the readability and usability of the generated
coverage HTML by making the list of files be a table.
2020-03-04 00:59:37 +00:00
Alberto Bertogli
28cb9169cc test: Add SPF integration test
This patch adds a new integration test to cover SPF checks. The main
goal is not to cover the SPF parsing, since that's handled by the
library already, but the higher level aspects: that the mails are indeed
rejected, that the DSN looks reasonable, etc.
2019-12-01 22:13:31 +00:00
Alberto Bertogli
a75eabbc22 test: Generate a prettier coverage report
To make the coverage report a bit more accessible and easier to
navigate, this patch makes the coverage tests generate a new HTML
coverage report (in addition to the classic variant).
2019-10-26 01:56:33 +01:00
Alberto Bertogli
3aa7bb4555 test: Adjust Dockerfile to support modules
When modules are configured, go will now attempt to fetch them on build,
using the proxy.

That causes the integration tests run via Docker to fail, because go
build attempts to contact the proxy within the test environment, which
has the external DNS resolvers disabled.

This patch fixes the problem by adjusting the pre-run fetch to use the
new modules system. We fetch the dependencies to the cache so that when
they're needed within the environment, they are present and there's no
need to use the proxy.
2019-10-19 13:06:26 +01:00
Alberto Bertogli
34ade50374 test: Work around msmtp having setgid permissions
In some distributions, including newer Debian versions, msmtp is
installed as setgid.

That prevents $HOSTALIASES from being honoured, which breaks the tests.

This patch works around the problem by creating a copy of the binary,
which will not have the setgid bit set.
2019-08-31 01:27:19 +01:00
Alberto Bertogli
0f487e5fb5 test: Remove dependency on Python 2
Python 2 is approaching end of life, and we only need it to run
the mail_diff test utility.

This patch updates mail_diff to run on Python 3, which only needed minor
changes.
2019-08-30 09:46:46 +01:00
Alberto Bertogli
0d3bbe11c0 test: In the docker entrypoint, wait for minidns to come up
The tests might start running and attempting to do DNS resolutions
before minidns has come up, which can cause false positives and flaky
tests.

This patch makes the entrypoint wait until minidns has come up, to fix
the problem.
2019-08-04 04:55:07 +01:00
Alberto Bertogli
abf91eac8d test: Make mail_diff support comparing multipart messages
In upcoming patches we will want to compare mime-multipart messages, so
this patch extends the mail_diff test helper to support it.
2019-01-18 14:54:29 +00:00
Alberto Bertogli
dd7cfaebf2 test: Use minidns in the Docker tests
The integration tests depend on having a DNS server that resolves
"localhost", which is unfortunate but currently unavoidable given
glibc's limitations ($HOSTALIASES only works on DNS-level aliases, and
does not do lookups in /etc/hosts).

Even under docker, this makes the tests depend on the DNS server, and
whether it resolves localhost or not.

In order to make the docker tests more hermetic and isolated from the
environment, this patch introduces a docker entrypoint that, within the
container, will launch minidns and override /etc/resolv.conf to use it.

This guarantees that the tests will be able to resolve localhost, and
also avoid accidental reliance on external DNS zones.
2018-11-30 10:03:48 +00:00
Alberto Bertogli
a0ae5fb41b test/util: Add mini DNS server for testing purposes
This is a mini-DNS server for testing purposes.

This can be used to set up hermetic tests in containers, and work around
glibc's limitation of being unable to create per-process host aliases.
2018-11-30 10:03:48 +00:00
Alberto Bertogli
4ecc5461d3 Add driusan/dkim integration example and tests
This patch adds DKIM signing using https://github.com/driusan/dkim tools
to the example hook.

It also adds an optional integration test to exercise signing and
verification, and corresponding documentation.
2018-11-30 10:03:48 +00:00
Alberto Bertogli
cf81fbee74 dovecot: Add tests for more error cases
This patch adds more tests for the dovecot library, in particular:
 - Protocol errors (invalid versions, etc.).
 - Invalid command (cli-specific test).
 - Connection breakups.
2018-06-04 01:34:32 +01:00
Alberto Bertogli
029bca7013 test: Add TLS tracking integration test
This patch adds a new test, which verifies the TLS tracking.

Because we need to simulate SPF records, and Go does not support fully
intercepting DNS lookups yet, this test relies on dnsmasq to provide a
DNS resolver.

In the future, once Go supports DNS lookup interception, we can get rid
of this additional dependency.
2018-06-02 19:31:28 +01:00
Alberto Bertogli
88789c8cde test: Properly exit with non-0 status on errors
SIGTERM can happen normally in our tests, and the current code has a
trap that makes it trigger a clean exit.

This causes some errors to be masked, as we end up calling "exit 0" when
they occur. The error message will still be displayed, but the caller
script will assume it worked.

This patch fixes the problem by adjusting the bash signal handlers, so
that we ignore SIGTERM (so bash does not get killed by the exit handler)
and exit with error on SIGINT (triggered by ctrl-c).

Note that under some conditions the SIGTERM trap is not necessary, but
this depends on the environment.
2018-04-09 22:51:01 +01:00
Alberto Bertogli
1b28734e44 test: Make "chamuyero" work in utf8 regardless of the environment
Our chamuyero tests involve reading and writing utf8. This usually
works, but is dependent on the environment: on LC_ALL=POSIX environment,
for example, Python enforces ascii as the default encoding, and the
tests break.

So this patch makes chamuyero explicitly set utf8 encodings in stdout
and all the sockets.
2018-03-26 01:58:47 +01:00
Alberto Bertogli
b0011f5a51 test: Add stress tests
This patch introduces stress tests, which put load on the daemon to help
sanity check its behaviour under stress.

They are separate from the existing integration tests, which focus on
correctness.

Two tests are included here: a load test, which sends emails repeatedly;
and a connection test which opens as many conections as possible.
2018-03-26 01:58:47 +01:00