aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-17 21:26:20 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-17 21:26:20 +0200
commited1c15929d2eb2dbc6432986c26661bf1549964a (patch)
tree4f04934edef686b19d16e1bb2f79bb5c15e142de /tests
parent380195f7cd5e57acf2c1cf2bc41069e6b0b979ed (diff)
downloadbokf-ed1c15929d2eb2dbc6432986c26661bf1549964a.tar.gz
bokf-ed1c15929d2eb2dbc6432986c26661bf1549964a.zip
Add native TLS transport, TLS clients and lego cert sidecarv0.1.1
- bokfd: optional TLS listener (OpenSSL), certificate reload on change - clients: tls:host:port targets with chain and host verification - compose: port 8788 and an INWX/lego renewal sidecar - Makefile: header dependency tracking (-MMD -MP)
Diffstat (limited to 'tests')
-rw-r--r--tests/test_core.c127
-rw-r--r--tests/tls_test_cert.pem11
-rw-r--r--tests/tls_test_key.pem5
3 files changed, 143 insertions, 0 deletions
diff --git a/tests/test_core.c b/tests/test_core.c
index db3bfb2..b2c828d 100644
--- a/tests/test_core.c
+++ b/tests/test_core.c
@@ -1,10 +1,17 @@
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <openssl/ssl.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
+#include "client.h"
#include "config.h"
#include "db.h"
#include "protocol.h"
@@ -170,6 +177,124 @@ static int login(const char *user, const char *pass)
return ok;
}
+static void echo_child(int wfd, int tls)
+{
+ int sfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sfd < 0)
+ _exit(1);
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof sa);
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sa.sin_port = 0;
+ if (bind(sfd, (struct sockaddr *)&sa, sizeof sa) != 0 ||
+ listen(sfd, 1) != 0)
+ _exit(1);
+ socklen_t slen = sizeof sa;
+ if (getsockname(sfd, (struct sockaddr *)&sa, &slen) != 0)
+ _exit(1);
+ int port = ntohs(sa.sin_port);
+ if (write(wfd, &port, sizeof port) != (ssize_t)sizeof port)
+ _exit(1);
+ close(wfd);
+ int cfd = accept(sfd, NULL, NULL);
+ if (cfd < 0)
+ _exit(1);
+ char buf[256];
+ if (tls) {
+ SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
+ if (!ctx ||
+ SSL_CTX_use_certificate_chain_file(ctx,
+ "tests/tls_test_cert.pem") != 1 ||
+ SSL_CTX_use_PrivateKey_file(ctx, "tests/tls_test_key.pem",
+ SSL_FILETYPE_PEM) != 1)
+ _exit(1);
+ SSL *ssl = SSL_new(ctx);
+ SSL_set_fd(ssl, cfd);
+ if (SSL_accept(ssl) != 1)
+ _exit(1);
+ size_t got = 0;
+ while (got < sizeof buf) {
+ int n = SSL_read(ssl, buf + got, (int)(sizeof buf - got));
+ if (n <= 0)
+ break;
+ got += (size_t)n;
+ if (memchr(buf, '\n', got))
+ break;
+ }
+ (void)SSL_write(ssl, "tls-ok", 6);
+ SSL_shutdown(ssl);
+ SSL_free(ssl);
+ SSL_CTX_free(ctx);
+ } else {
+ size_t got = 0;
+ while (got < sizeof buf) {
+ ssize_t n = read(cfd, buf + got, sizeof buf - got);
+ if (n <= 0)
+ break;
+ got += (size_t)n;
+ if (memchr(buf, '\n', got))
+ break;
+ }
+ (void)write(cfd, "tcp-ok", 6);
+ }
+ close(cfd);
+ close(sfd);
+ _exit(0);
+}
+
+/* variant 0: plaintext tcp; 1: TLS trusting the test CA; 2: TLS without the
+ CA, which must fail certificate verification. */
+static void test_transport(void)
+{
+ signal(SIGPIPE, SIG_IGN);
+ for (int variant = 0; variant < 3; variant++) {
+ int tls = variant > 0;
+ int pfd[2];
+ if (pipe(pfd) != 0) {
+ CHECK(0 && "pipe");
+ return;
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(pfd[0]);
+ echo_child(pfd[1], tls);
+ }
+ close(pfd[1]);
+ int port = 0;
+ if (read(pfd[0], &port, sizeof port) != (ssize_t)sizeof port)
+ port = 0;
+ close(pfd[0]);
+ if (port <= 0) {
+ CHECK(0 && "no test server port");
+ waitpid(pid, NULL, 0);
+ continue;
+ }
+ if (variant == 1)
+ setenv("BOKFD_TLS_CA", "tests/tls_test_cert.pem", 1);
+ else
+ unsetenv("BOKFD_TLS_CA");
+ char target[64];
+ snprintf(target, sizeof target, "%s:localhost:%d",
+ tls ? "tls" : "tcp", port);
+ struct client_conn c;
+ if (variant == 2) {
+ CHECK(client_connect(target, &c) != 0);
+ waitpid(pid, NULL, 0);
+ continue;
+ }
+ CHECK(client_connect(target, &c) == 0);
+ CHECK(client_send_line(&c, "{\"v\":1}") == 0);
+ char *line = client_read_line(&c);
+ CHECK(line && strncmp(line, tls ? "tls-ok" : "tcp-ok", 6) == 0);
+ if (!line)
+ fprintf(stderr, "transport error: %s\n", client_last_error());
+ free(line);
+ client_close(&c);
+ waitpid(pid, NULL, 0);
+ }
+}
+
int main(void)
{
char tmpdir[] = "/tmp/bokf-test-XXXXXX";
@@ -1059,6 +1184,8 @@ int main(void)
CHECK(jint(d, "result.checked") > 20);
yyjson_doc_free(d);
+ test_transport();
+
/* rate limiting must stay last: it blocks the login key */
for (int i = 0; i < 5; i++)
CHECK(!login("admin", "wrong"));
diff --git a/tests/tls_test_cert.pem b/tests/tls_test_cert.pem
new file mode 100644
index 0000000..c559354
--- /dev/null
+++ b/tests/tls_test_cert.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBmTCCAT+gAwIBAgIUGXc7xLB5t5mnvBH4vPYvo6ACfowwCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDkxNzE4MjAzMloXDTM2MDkxNDE4
+MjAzMlowFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
+AQcDQgAE66+gpPbkPDOANXFk3UfqpE3djA17Z/EzZPBgmuKpbNyZftyjESkSf0Ra
+LG3sZ97J9SMFWJJwLtB70I3dZO+NxaNvMG0wHQYDVR0OBBYEFHv6wMxHetBdatyF
+ydr8XLg+aIVQMB8GA1UdIwQYMBaAFHv6wMxHetBdatyFydr8XLg+aIVQMA8GA1Ud
+EwEB/wQFMAMBAf8wGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMAoGCCqGSM49
+BAMCA0gAMEUCIDACquqAztvZ4gMH5MlbuZ8Hkqie1txpmIl6yW9qCm9MAiEAllS3
+LDzwrm8g5KgLAgURVZcdBLth3HXjYgQQrn/uwEQ=
+-----END CERTIFICATE-----
diff --git a/tests/tls_test_key.pem b/tests/tls_test_key.pem
new file mode 100644
index 0000000..b084830
--- /dev/null
+++ b/tests/tls_test_key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgjif72KjB8IkhmHF2
+FFNqe3eSRHi5cXOIba+FC5/Jd4ehRANCAATrr6Ck9uQ8M4A1cWTdR+qkTd2MDXtn
+8TNk8GCa4qls3Jl+3KMRKRJ/RFosbexn3sn1IwVYknAu0HvQjd1k743F
+-----END PRIVATE KEY-----