mirror of
https://github.com/mjl-/mox.git
synced 2025-02-05 10:48:29 +03:00
Add integration test for ALPN
This commit is contained in:
parent
2db7323921
commit
5dd88e7d19
4 changed files with 155 additions and 7 deletions
|
@ -26,6 +26,8 @@ services:
|
|||
condition: service_healthy
|
||||
localserve:
|
||||
condition: service_healthy
|
||||
moxacmepebblealpn:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
mailnet1:
|
||||
ipv4_address: 172.28.1.50
|
||||
|
@ -83,6 +85,31 @@ services:
|
|||
mailnet1:
|
||||
ipv4_address: 172.28.1.20
|
||||
|
||||
# Third mox instance that uses ACME with pebble and has ALPN enabled.
|
||||
moxacmepebblealpn:
|
||||
hostname: moxacmepebblealpn.mox1.example
|
||||
domainname: mox3.example
|
||||
image: mox_integration_moxmail
|
||||
environment:
|
||||
MOX_UID: "${MOX_UID}"
|
||||
command: ["sh", "-c", "/integration/moxacmepebblealpn.sh"]
|
||||
volumes:
|
||||
- ./testdata/integration/resolv.conf:/etc/resolv.conf
|
||||
- ./testdata/integration:/integration
|
||||
healthcheck:
|
||||
test: netstat -nlt | grep ':25 '
|
||||
interval: 1s
|
||||
timeout: 1s
|
||||
retries: 10
|
||||
depends_on:
|
||||
dns:
|
||||
condition: service_healthy
|
||||
acmepebble:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
mailnet1:
|
||||
ipv4_address: 172.28.1.80
|
||||
|
||||
localserve:
|
||||
hostname: localserve.mox1.example
|
||||
domainname: mox1.example
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -190,3 +192,77 @@ a message.
|
|||
})
|
||||
log.Print("success", slog.Any("duration", time.Since(t0)))
|
||||
}
|
||||
|
||||
func expectReadAfter2s(t *testing.T, hostport string, nextproto string, expected string) {
|
||||
tlsConfig := &tls.Config{
|
||||
NextProtos: []string{
|
||||
nextproto,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", hostport, tlsConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("error dialing moxacmepebblealpn 443 for %s: %v", nextproto, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
rdr := bufio.NewReader(conn)
|
||||
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
line, err := rdr.ReadString('\n')
|
||||
if err != nil {
|
||||
t.Fatalf("error reading from %s connection: %v", nextproto, err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(line, expected) {
|
||||
t.Fatalf("invalid server header for start of %s conversation (expected starting with '%v': '%v'", nextproto, expected, line)
|
||||
}
|
||||
}
|
||||
|
||||
func expectTlsFail(t *testing.T, hostport string, nextproto string) {
|
||||
tlsConfig := &tls.Config{
|
||||
NextProtos: []string{
|
||||
nextproto,
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", hostport, tlsConfig)
|
||||
expected := "tls: no application protocol"
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
t.Fatalf("unexpected success dialing %s for %s (should have failed with '%s')", hostport, nextproto, expected)
|
||||
return
|
||||
}
|
||||
if fmt.Sprintf("%v", err) == expected {
|
||||
t.Fatalf("unexpected error dialing %s for %s (expected %s): %v", hostport, nextproto, expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestALPN(t *testing.T) {
|
||||
known_available_http_file := "https://%s/.well-known/mta-sts.txt"
|
||||
log := mlog.New("integration", nil)
|
||||
mlog.Logfmt = true
|
||||
// ALPN should work when enabled.
|
||||
alpnhost := "moxacmepebblealpn.mox1.example:443"
|
||||
log.Info("trying IMAP via ALPN (should succeed)", slog.String("host", alpnhost))
|
||||
expectReadAfter2s(t, alpnhost, "imap", "* OK ")
|
||||
log.Info("trying SMTP via ALPN (should succeed)", slog.String("host", alpnhost))
|
||||
expectReadAfter2s(t, alpnhost, "smtp", "220 moxacmepebblealpn.mox1.example ESMTP ")
|
||||
log.Info("trying HTTP (should succeed)", slog.String("host", alpnhost))
|
||||
_, err := http.Get(fmt.Sprintf(known_available_http_file, alpnhost))
|
||||
if err != nil {
|
||||
t.Fatalf("error checking for HTTP response on ALPN host (expected nil): %v", err)
|
||||
}
|
||||
|
||||
// ALPN should not work when not enabled.
|
||||
nonalpnhost := "moxacmepebble.mox1.example:443"
|
||||
log.Info("trying IMAP via ALPN (should fail)", slog.String("host", nonalpnhost))
|
||||
expectTlsFail(t, nonalpnhost, "imap")
|
||||
log.Info("trying SMTP via ALPN (should fail)", slog.String("host", nonalpnhost))
|
||||
expectTlsFail(t, nonalpnhost, "smtp")
|
||||
log.Info("trying HTTP (should succeed)", slog.String("host", nonalpnhost))
|
||||
_, err = http.Get(fmt.Sprintf(known_available_http_file, nonalpnhost))
|
||||
if err != nil {
|
||||
t.Fatalf("error checking for HTTP response on non-ALPN host (expected nil): %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
15
testdata/integration/example.zone
vendored
15
testdata/integration/example.zone
vendored
|
@ -5,13 +5,14 @@ $TTL 5m
|
|||
|
||||
@ NS dns.example.
|
||||
|
||||
moxacmepebble.mox1 A 172.28.1.10
|
||||
moxmail2.mox2 A 172.28.1.20
|
||||
dns A 172.28.1.30
|
||||
acmepebble A 172.28.1.40
|
||||
test A 172.28.1.50
|
||||
localserve.mox1 A 172.28.1.60
|
||||
postfixmail.postfix A 172.28.1.70
|
||||
moxacmepebble.mox1 A 172.28.1.10
|
||||
moxmail2.mox2 A 172.28.1.20
|
||||
dns A 172.28.1.30
|
||||
acmepebble A 172.28.1.40
|
||||
test A 172.28.1.50
|
||||
localserve.mox1 A 172.28.1.60
|
||||
postfixmail.postfix A 172.28.1.70
|
||||
moxacmepebblealpn.mox1 A 172.28.1.80
|
||||
|
||||
postfix MX 10 postfixmail.postfix.example.
|
||||
postfixdkim0._domainkey.postfix TXT "v=DKIM1;h=sha256;t=s;k=ed25519;p=a4IsBTuMsSQjU+xVyx8KEd8eObis4FrCiV72OaEkvDY="
|
||||
|
|
44
testdata/integration/moxacmepebblealpn.sh
vendored
Executable file
44
testdata/integration/moxacmepebblealpn.sh
vendored
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
set -x # print commands
|
||||
set -e # exit on failed command
|
||||
|
||||
apk add unbound curl
|
||||
|
||||
(rm -r /tmp/mox 2>/dev/null || exit 0) # clean slate
|
||||
mkdir /tmp/mox
|
||||
cd /tmp/mox
|
||||
mox quickstart -skipdial moxtest1@mox1.example "$MOX_UID" > output.txt
|
||||
|
||||
cp config/mox.conf config/mox.conf.orig
|
||||
sed -i -e 's/letsencrypt:/pebble:/g' -e 's/: letsencrypt/: pebble/g' -e 's,DirectoryURL: https://acme-v02.api.letsencrypt.org/directory,DirectoryURL: https://acmepebble.example:14000/dir,' -e 's/SMTP:$/SMTP:\n\t\t\tFirstTimeSenderDelay: 1s/' -e 's/Submissions:$/Submissions:\n\t\t\tEnableOnHTTPS: true/' -e 's/IMAPS:$/IMAPS:\n\t\t\tEnableOnHTTPS: true/' config/mox.conf
|
||||
cat <<EOF >>config/mox.conf
|
||||
|
||||
TLS:
|
||||
CA:
|
||||
CertFiles:
|
||||
# So certificates from moxmail2 are trusted, and pebble's certificate is trusted.
|
||||
- /integration/tls/ca.pem
|
||||
EOF
|
||||
# Recognize postfix@mox1.example as destination, and that it is a forwarding destination.
|
||||
# Postfix seems to keep the mailfrom when forwarding, so we match on that verifieddomain (but using DKIM).
|
||||
sed -i -e 's/moxtest1@mox1.example: nil/moxtest1@mox1.example: nil\n\t\t\tpostfix@mox1.example:\n\t\t\t\tRulesets:\n\t\t\t\t\t-\n\t\t\t\t\t\tSMTPMailFromRegexp: .*\n\t\t\t\t\t\tVerifiedDomain: mox1.example\n\t\t\t\t\t\tIsForward: true\n\t\t\t\t\t\tMailbox: Inbox/' config/domains.conf
|
||||
|
||||
(
|
||||
cat /integration/example.zone;
|
||||
sed -n '/^;/,/will be suggested/p' output.txt |
|
||||
# allow sending from postfix for mox1.example.
|
||||
sed 's/mox1.example. *TXT "v=spf1 ip4:172.28.1.10 mx ~all"/mox1.example. TXT "v=spf1 ip4:172.28.1.10 ip4:172.28.1.70 mx ~all"/'
|
||||
) >/integration/example-integration.zone
|
||||
unbound-control -s 172.28.1.30 reload # reload unbound with zone file changes
|
||||
|
||||
CURL_CA_BUNDLE=/integration/tls/ca.pem curl -o /integration/tmp-pebble-ca.pem https://acmepebble.example:15000/roots/0
|
||||
|
||||
mox -checkconsistency serve &
|
||||
while true; do
|
||||
if test -e data/ctl; then
|
||||
echo -n accountpass1234 | mox setaccountpassword moxtest1
|
||||
break
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
wait
|
Loading…
Reference in a new issue