Skip to content

Commit 150af10

Browse files
committed
test(stream): cover upstream mTLS client cert via C-API
Add t/stream/upstream_mtls.t exercising resty.apisix.stream.upstream set_cert_and_key over a real stream proxy to a TLS upstream that requires a client certificate (ssl_verify_client on): - valid client cert+key -> mTLS handshake succeeds (ssl_client_verify SUCCESS) - missing private key -> rejected by the Lua wrapper before handshake - wrong client cert (not signed by the upstream CA) -> upstream verify error - repeated set_cert_and_key -> still handshakes (covers the re-entrant cleanup path in ngx_stream_apisix_upstream_set_cert_and_key) Mirrors the existing http t/upstream_mtls.t for the stream subsystem; the feature previously had no test coverage.
1 parent 47f2676 commit 150af10

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

t/stream/upstream_mtls.t

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use t::APISIX_NGINX 'no_plan';
2+
3+
repeat_each(2);
4+
5+
add_block_preprocessor(sub {
6+
my ($block) = @_;
7+
8+
if (!$block->http_config) {
9+
my $http_config = <<'_EOC_';
10+
server {
11+
listen 1995 ssl;
12+
server_name admin.apisix.dev;
13+
ssl_certificate ../../certs/mtls_server.crt;
14+
ssl_certificate_key ../../certs/mtls_server.key;
15+
ssl_client_certificate ../../certs/mtls_ca.crt;
16+
ssl_verify_client on;
17+
18+
server_tokens off;
19+
20+
location / {
21+
content_by_lua_block {
22+
ngx.say("client verify: ", ngx.var.ssl_client_verify)
23+
}
24+
}
25+
}
26+
27+
_EOC_
28+
29+
$block->set_value("http_config", $http_config);
30+
}
31+
});
32+
33+
run_tests();
34+
35+
__DATA__
36+
37+
=== TEST 1: stream upstream mTLS - send client cert and key, handshake succeeds
38+
--- stream_server_config
39+
preread_by_lua_block {
40+
local up = require("resty.apisix.stream.upstream")
41+
local ssl = require("ngx.ssl")
42+
43+
local f = assert(io.open("t/certs/mtls_client.crt"))
44+
local cert_data = f:read("*a")
45+
f:close()
46+
local cert = assert(ssl.parse_pem_cert(cert_data))
47+
48+
f = assert(io.open("t/certs/mtls_client.key"))
49+
local key_data = f:read("*a")
50+
f:close()
51+
local key = assert(ssl.parse_pem_priv_key(key_data))
52+
53+
assert(up.set_tls())
54+
assert(up.set_cert_and_key(cert, key))
55+
}
56+
proxy_pass 127.0.0.1:1995;
57+
proxy_ssl_server_name on;
58+
proxy_ssl_name admin.apisix.dev;
59+
--- stream_request eval
60+
"GET / HTTP/1.0\r\nHost: admin.apisix.dev\r\n\r\n"
61+
--- stream_response_like: client verify: SUCCESS
62+
63+
64+
65+
=== TEST 2: missing private key is rejected before the handshake
66+
--- stream_server_config
67+
preread_by_lua_block {
68+
local up = require("resty.apisix.stream.upstream")
69+
local ssl = require("ngx.ssl")
70+
71+
local f = assert(io.open("t/certs/mtls_client.crt"))
72+
local cert_data = f:read("*a")
73+
f:close()
74+
local cert = assert(ssl.parse_pem_cert(cert_data))
75+
76+
assert(up.set_tls())
77+
local ok, err = up.set_cert_and_key(cert, nil)
78+
if not ok then
79+
ngx.log(ngx.ERR, "set_cert_and_key failed: ", err)
80+
end
81+
}
82+
proxy_pass 127.0.0.1:1995;
83+
proxy_ssl_server_name on;
84+
proxy_ssl_name admin.apisix.dev;
85+
--- stream_request eval
86+
"GET / HTTP/1.0\r\nHost: admin.apisix.dev\r\n\r\n"
87+
--- error_log
88+
set_cert_and_key failed: both client certificate and private key should be given
89+
90+
91+
92+
=== TEST 3: wrong client certificate is rejected by the upstream
93+
--- stream_server_config
94+
preread_by_lua_block {
95+
local up = require("resty.apisix.stream.upstream")
96+
local ssl = require("ngx.ssl")
97+
98+
-- apisix.crt is not signed by mtls_ca.crt, so the upstream rejects it
99+
local f = assert(io.open("t/certs/apisix.crt"))
100+
local cert_data = f:read("*a")
101+
f:close()
102+
local cert = assert(ssl.parse_pem_cert(cert_data))
103+
104+
f = assert(io.open("t/certs/apisix.key"))
105+
local key_data = f:read("*a")
106+
f:close()
107+
local key = assert(ssl.parse_pem_priv_key(key_data))
108+
109+
assert(up.set_tls())
110+
assert(up.set_cert_and_key(cert, key))
111+
}
112+
proxy_pass 127.0.0.1:1995;
113+
proxy_ssl_server_name on;
114+
proxy_ssl_name admin.apisix.dev;
115+
--- stream_request eval
116+
"GET / HTTP/1.0\r\nHost: admin.apisix.dev\r\n\r\n"
117+
--- error_log
118+
client SSL certificate verify error
119+
120+
121+
122+
=== TEST 4: set_cert_and_key called repeatedly still handshakes
123+
--- stream_server_config
124+
preread_by_lua_block {
125+
local up = require("resty.apisix.stream.upstream")
126+
local ssl = require("ngx.ssl")
127+
128+
local f = assert(io.open("t/certs/mtls_client.crt"))
129+
local cert_data = f:read("*a")
130+
f:close()
131+
local cert = assert(ssl.parse_pem_cert(cert_data))
132+
133+
f = assert(io.open("t/certs/mtls_client.key"))
134+
local key_data = f:read("*a")
135+
f:close()
136+
local key = assert(ssl.parse_pem_priv_key(key_data))
137+
138+
assert(up.set_tls())
139+
for _ = 1, 5 do
140+
assert(up.set_cert_and_key(cert, key))
141+
end
142+
}
143+
proxy_pass 127.0.0.1:1995;
144+
proxy_ssl_server_name on;
145+
proxy_ssl_name admin.apisix.dev;
146+
--- stream_request eval
147+
"GET / HTTP/1.0\r\nHost: admin.apisix.dev\r\n\r\n"
148+
--- stream_response_like: client verify: SUCCESS

0 commit comments

Comments
 (0)