Changeset

13816:4122978f2575 13.0

spec/tls: Add TLS/certificate integration tests These tests help to verify that various configurations translate into the expected running TLS setups. Specifically right now we are checking the correct certificate is served.
author Matthew Wild <mwild1@gmail.com>
date Thu, 03 Apr 2025 15:11:58 +0100
parents 13814:98e68a68c1da
children 13817:a5f6943a1abd 13818:8a7dbb291b02
files GNUmakefile spec/tls/README spec/tls/config1/assert.sh spec/tls/config1/prepare.sh spec/tls/config1/prosody.cfg.lua spec/tls/config2/assert.sh spec/tls/config2/prepare.sh spec/tls/config2/prosody.cfg.lua spec/tls/config3/assert.sh spec/tls/config3/prepare.sh spec/tls/config3/prosody.cfg.lua spec/tls/lib.sh spec/tls/run.sh
diffstat 13 files changed, 237 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Thu Apr 03 12:03:24 2025 +0100
+++ b/GNUmakefile	Thu Apr 03 15:11:58 2025 +0100
@@ -122,6 +122,9 @@
 	$(RUNWITH) prosodyctl --config ./spec/scansion/prosody.cfg.lua stop \
 	exit $$R
 
+integration-test-tls: all
+	cd ./spec/tls && ./run.sh
+
 coverage:
 	-rm -- luacov.*
 	$(BUSTED) --lua=$(RUNWITH) -c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/README	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,11 @@
+These tests check that SSL/TLS configuration is working as expected.
+
+Just run ./run.sh in this directory (or from the top level,
+`make integration-test-tls`.
+
+Known issues:
+  - The tests do not thorougly clean up after themselves (certs, logs, etc.).
+    This is partly intentional, so they can be inspected in case of failures.
+  - Certs are regenerated every time. Could be smarter about this. But it also
+    helps to guard against incorrect Prosody instances running and hogging the
+    ports, etc.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config1/assert.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+#set -x
+
+. ../lib.sh
+
+expect_cert "certs/example.com.crt" "localhost:5222" "example.com" "xmpp"
+expect_cert "certs/share.example.com.crt" "localhost:5281" "share.example.com" "tls"
+
+exit "$failures"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config1/prepare.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+certs="./certs"
+
+for domain in {,share.}example.com; do
+	openssl req -x509 \
+	  -newkey rsa:4096 \
+	  -keyout "${certs}/${domain}.key" \
+	  -out "${certs}/${domain}.crt" \
+	  -sha256 \
+	  -days 365 \
+	  -nodes \
+	  -subj "/CN=${domain}" 2>/dev/null;
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config1/prosody.cfg.lua	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,6 @@
+Include "prosody-default.cfg.lua"
+
+VirtualHost "example.com"
+	enabled = true
+
+Component "share.example.com" "http_file_share"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config2/assert.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+#set -x
+
+. ../lib.sh
+
+expect_cert "certs/xmpp.example.com.crt" "localhost:5281" "xmpp.example.com" "tls"
+expect_cert "certs/example.com.crt" "localhost:5222" "example.com" "xmpp"
+
+exit "$failures"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config2/prepare.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+certs="./certs"
+
+for domain in {,xmpp.}example.com; do
+	openssl req -x509 \
+	  -newkey rsa:4096 \
+	  -keyout "${certs}/${domain}.key" \
+	  -out "${certs}/${domain}.crt" \
+	  -sha256 \
+	  -days 365 \
+	  -nodes \
+	  -subj "/CN=${domain}" 2>/dev/null;
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config2/prosody.cfg.lua	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,6 @@
+Include "prosody-default.cfg.lua"
+
+VirtualHost "example.com"
+	enabled = true
+	modules_enabled = { "http" }
+	http_host = "xmpp.example.com"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config3/assert.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+#set -x
+
+. ../lib.sh
+
+expect_cert "certs/xmpp.example.com.crt" "localhost:5281" "xmpp.example.com" "tls"
+expect_cert "certs/example.com.crt" "localhost:5222" "example.com" "xmpp"
+expect_cert "certs/example.com.crt" "localhost:5223" "example.com" "xmpps"
+
+# Weirdly configured host, just to test manual override behaviour
+expect_cert "certs/example.com.crt" "localhost:5222" "example.net" "xmpp"
+expect_cert "certs/example.com.crt" "localhost:5222" "example.net" "xmpp"
+expect_cert "certs/example.com.crt" "localhost:5223" "example.net" "tls"
+expect_cert "certs/example.com.crt" "localhost:5281" "example.net" "tls"
+
+# Three domains using a single cert with SANs
+expect_cert "certs/example.org.crt" "localhost:5222" "example.org" "xmpp"
+expect_cert "certs/example.org.crt" "localhost:5223" "example.org" "xmpps"
+expect_cert "certs/example.org.crt" "localhost:5269" "example.org" "xmpp-server"
+expect_cert "certs/example.org.crt" "localhost:5269" "share.example.org" "xmpp-server"
+expect_cert "certs/example.org.crt" "localhost:5269" "groups.example.org" "xmpp-server"
+expect_cert "certs/example.org.crt" "localhost:5281" "share.example.org" "tls"
+
+exit "$failures"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config3/prepare.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+certs="./certs"
+
+for domain in {,xmpp.}example.com example.net; do
+	openssl req -x509 \
+	  -newkey rsa:4096 \
+	  -keyout "${certs}/${domain}.key" \
+	  -out "${certs}/${domain}.crt" \
+	  -sha256 \
+	  -days 365 \
+	  -nodes \
+	  -quiet \
+	  -subj "/CN=${domain}" 2>/dev/null;
+done
+
+for domain in example.org; do
+	openssl req -x509 \
+	  -newkey rsa:4096 \
+	  -keyout "${certs}/${domain}.key" \
+	  -out "${certs}/${domain}.crt" \
+	  -sha256 \
+	  -days 365 \
+	  -nodes \
+	  -subj "/CN=${domain}" \
+	  -addext "subjectAltName = DNS:${domain}, DNS:groups.${domain}, DNS:share.${domain}" \
+	  2>/dev/null;
+done
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/config3/prosody.cfg.lua	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,28 @@
+Include "prosody-default.cfg.lua"
+
+c2s_direct_tls_ports = { 5223 }
+
+VirtualHost "example.com"
+	enabled = true
+	modules_enabled = { "http" }
+	http_host = "xmpp.example.com"
+
+VirtualHost "example.net"
+	ssl = {
+		certificate = "certs/example.com.crt";
+		key = "certs/example.com.key";
+	}
+
+	https_ssl = {
+		certificate = "certs/example.com.crt";
+		key = "certs/example.com.key";
+	}
+
+	c2s_direct_tls_ssl = {
+		certificate = "certs/example.com.crt";
+		key = "certs/example.com.key";
+	}
+
+VirtualHost "example.org"
+Component "share.example.org" "http_file_share"
+Component "groups.example.org" "muc"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/lib.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+test_name="$(basename "$PWD")"
+export failures=0
+
+get_net_cert () {
+	address="${1?}"
+	sni="${2?}"
+	proto="${3?}"
+	local flags=()
+	case "$proto" in
+		"xmpp") flags=(-starttls xmpp -name "$sni");;
+		"xmpps") flags=(-alpn xmpp-client);;
+		"xmpp-server") flags=(-starttls xmpp-server -name "$sni");;
+		"xmpps-server") flags=(-alpn xmpp-server);;
+		"tls") ;;
+		*) printf "EE: Unknown protocol: %s\n" "$proto" >&2; exit 1;;
+	esac
+	openssl s_client -connect "$address" -servername "$sni" "${flags[@]}" 2>/dev/null </dev/null |  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'
+}
+
+get_file_cert () {
+	fn="${1?}"
+	sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' "$fn"
+}
+
+expect_cert () {
+	fn="${1?}"
+	address="${2?}"
+	sni="${3?}"
+	proto="${4?}"
+	net_cert="$(get_net_cert "$address" "$sni" "$proto")"
+	file_cert="$(get_file_cert "$fn")"
+	if [[ "$file_cert" != "$net_cert" ]]; then
+		echo "---"
+		echo "NOT OK: $test_name: Expected $fn on $address (SNI $sni)"
+		echo "Received:"
+		openssl x509 -in <(echo "$net_cert") -text
+		echo "---"
+		failures=1;
+		return 1;
+	fi
+	echo "OK: $test_name: $fn observed on $address (SNI $sni)"
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/tls/run.sh	Thu Apr 03 15:11:58 2025 +0100
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+export LUA_PATH="../../../?.lua;;"
+export LUA_CPATH="../../../?.so;;"
+
+any_failed=0
+
+for config in config*; do
+	echo "# Preparing $config"
+	pushd "$config";
+	cp ../../../prosody.cfg.lua.dist ./prosody-default.cfg.lua
+	echo 'VirtualHost "*" {pidfile = "prosody.pid";log={debug="prosody.log"}}' >> ./prosody-default.cfg.lua
+	ln -s ../../../plugins plugins
+	mkdir -p certs data
+	./prepare.sh
+	../../../prosody -D
+	sleep 1;
+	echo "# Testing $config"
+	./assert.sh
+	status=$?
+	../../../prosodyctl stop
+	rm plugins #prosody-default.cfg.lua
+	popd
+	if [[ "$status" != "0" ]]; then
+		echo -n "NOT ";
+		any_failed=1
+	fi
+	echo "OK: $config";
+done
+
+if [[ "$any_failed" != "0" ]]; then
+	echo "NOT OK: One or more TLS tests failed";
+	exit 1;
+fi
+
+echo "OK: All TLS tests passed";
+exit 0;