Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce tls.disable_http2_alpn() function #93

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
push:

env:
KONG_VERSION: master
KONG_VERSION: disable-h2-alpn-re
oowl marked this conversation as resolved.
Show resolved Hide resolved
BUILD_ROOT: ${{ github.workspace }}/kong/bazel-bin/build

concurrency:
Expand Down Expand Up @@ -135,7 +135,6 @@ jobs:
openssl version

prove -r t

- name: Run Test with Valgrind
run: |
source ${{ env.BUILD_ROOT }}/kong-dev-venv.sh
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,21 @@ describing the error will be returned.

[Back to TOC](#table-of-contents)

resty.kong.tls.disable\_http2\_alpn
----------------------------------------------------
**syntax:** *ok, err = resty.kong.tls.disable\_http2\_alpn()*

**context:** *client_hello_by_lua*

**subsystems:** *http*

Disables HTTP/2 ALPN negotiation for the current TLS connection. When called, the
connection will not negotiate HTTP/2 using ALPN and will fallback to HTTP/1.1 even though [`http2`](https://nginx.org/en/docs/http/ngx_http_v2_module.html#http2) directive is enabled.

This function returns `true` when the call is successful. Otherwise it returns `false` and a string describing the error.

[Back to TOC](#table-of-contents)

resty.kong.grpc.set\_authority
------------------------------
**syntax:** *ok, err = resty.kong.grpc.set_authority(new_authority)*
Expand Down
24 changes: 23 additions & 1 deletion lualib/resty/kong/tls.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ local get_string_buf = base.get_string_buf
local size_ptr = base.get_size_ptr()
local orig_get_request = base.get_request
local subsystem = ngx.config.subsystem
local errmsg = base.get_errmsg_ptr()
local FFI_OK = base.FFI_OK
base.allows_subsystem('http', 'stream')

local kong_lua_kong_ffi_get_full_client_certificate_chain
Expand All @@ -40,7 +42,7 @@ local kong_lua_kong_ffi_set_upstream_ssl_trusted_store
local kong_lua_kong_ffi_set_upstream_ssl_verify
local kong_lua_kong_ffi_set_upstream_ssl_verify_depth
local kong_lua_kong_ffi_get_socket_ssl

local kong_lua_kong_ffi_disable_http2_alpn
if subsystem == "http" then
ffi.cdef([[
typedef struct ssl_st SSL;
Expand All @@ -59,6 +61,7 @@ if subsystem == "http" then
int depth);
int ngx_http_lua_kong_ffi_get_socket_ssl(ngx_http_lua_socket_tcp_upstream_t *u,
void **ssl_conn);
int ngx_http_lua_ffi_ssl_disable_http2_alpn(ngx_http_request_t *r, char **err);
]])

kong_lua_kong_ffi_get_full_client_certificate_chain = C.ngx_http_lua_kong_ffi_get_full_client_certificate_chain
Expand All @@ -68,6 +71,7 @@ if subsystem == "http" then
kong_lua_kong_ffi_set_upstream_ssl_verify = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify
kong_lua_kong_ffi_set_upstream_ssl_verify_depth = C.ngx_http_lua_kong_ffi_set_upstream_ssl_verify_depth
kong_lua_kong_ffi_get_socket_ssl = C.ngx_http_lua_kong_ffi_get_socket_ssl
kong_lua_kong_ffi_disable_http2_alpn = C.ngx_http_lua_ffi_ssl_disable_http2_alpn

elseif subsystem == 'stream' then
ffi.cdef([[
Expand Down Expand Up @@ -314,6 +318,24 @@ do

error("unknown return code: " .. tostring(ret))
end

function _M.disable_http2_alpn()
oowl marked this conversation as resolved.
Show resolved Hide resolved
if get_phase() ~= "ssl_client_hello" then
error("API disabled in the current context")
end

local r = get_request()
if not r then
error("no request found")
end

local rc = kong_lua_kong_ffi_disable_http2_alpn(r, errmsg)
if rc == FFI_OK then
return true
end

return false, ffi_string(errmsg[0])
end
end

if ngx.config.subsystem == "stream" then
Expand Down
3 changes: 3 additions & 0 deletions src/ngx_http_lua_kong_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,8 @@ ngx_flag_t
ngx_http_lua_kong_get_upstream_ssl_verify(ngx_http_request_t *r,
ngx_flag_t proxy_ssl_verify);

ngx_flag_t
ngx_http_lua_kong_ssl_get_http2_alpn_enabled(ngx_ssl_connection_t *ssl,
ngx_flag_t enable_http2);

#endif /* _NGX_HTTP_LUA_KONG_MODULE_H_INCLUDED_ */
45 changes: 44 additions & 1 deletion src/ngx_http_lua_kong_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

#include "ngx_http_lua_kong_common.h"
#include "ngx_http_lua_socket_tcp.h"

#include "ngx_http_lua_ssl.h"
#include "ngx_http_lua_util.h"

/*
* disables session reuse for the current TLS connection, must be called
Expand Down Expand Up @@ -175,6 +176,48 @@ ngx_http_lua_kong_get_upstream_ssl_verify(ngx_http_request_t *r,
return ngx_lua_kong_ssl_get_upstream_ssl_verify(&ctx->ssl_ctx, proxy_ssl_verify);
}

ngx_flag_t
ngx_http_lua_kong_ssl_get_http2_alpn_enabled(ngx_ssl_connection_t *ssl,
ngx_flag_t enable_http2)
{
ngx_http_lua_ssl_ctx_t *cctx;

cctx = ngx_http_lua_ssl_get_ctx(ssl->connection);
if (cctx && cctx->disable_http2_alpn) {
return 0;
}

return enable_http2;
}

int
ngx_http_lua_ffi_ssl_disable_http2_alpn(ngx_http_request_t *r, char **err)
{
ngx_ssl_conn_t *ssl_conn;
ngx_http_lua_ssl_ctx_t *cctx;

if (r->connection == NULL || r->connection->ssl == NULL) {
*err = "bad request";
return NGX_ERROR;
}

ssl_conn = r->connection->ssl->connection;
if (ssl_conn == NULL) {
*err = "bad ssl conn";
return NGX_ERROR;
}

cctx = ngx_http_lua_ssl_get_ctx(ssl_conn);
if (cctx == NULL) {
*err = "bad lua context";
return NGX_ERROR;
}
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"lua ssl disable http2");
cctx->disable_http2_alpn = 1;

return NGX_OK;
}

#endif

Expand Down
167 changes: 167 additions & 0 deletions t/012-tls_disable_http2_alpn.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# vim:set ft= ts=4 sw=4 et:

use Test::Nginx::Socket::Lua;
use Cwd qw(cwd);

repeat_each(2);

plan tests => repeat_each() * (blocks() * 7 - 2);

my $pwd = cwd();

$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();

log_level('info');
no_long_string();
#no_diff();

run_tests();

__DATA__

=== TEST 1: normal http2 alpn
--- http_config
lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;";

server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
listen 60000 ssl;
server_name example.com;
ssl_certificate ../../cert/example.com.crt;
ssl_certificate_key ../../cert/example.com.key;
ssl_session_cache off;
ssl_session_tickets on;
server_tokens off;
http2 on;
ssl_client_hello_by_lua_block {
local tls = require("resty.kong.tls")
}
location /foo {
default_type 'text/plain';
content_by_lua_block {ngx.exit(200)}
more_clear_headers Date;
}
}
--- config
server_tokens off;
location /t {
content_by_lua_block {
local ngx_pipe = require "ngx.pipe"
local proc = ngx_pipe.spawn({'curl', '-vk', '--resolve', 'example.com:60000:127.0.0.1', 'https://example.com:60000'})
local stdout_data, err = proc:stdout_read_all()
if not stdout_data then
ngx.say(err)
return
end

local stderr_data, err = proc:stderr_read_all()
if not stderr_data then
ngx.say(err)
return
end

if string.find(stderr_data, "ALPN: server accepted h2") ~= nil then
ngx.say("alpn server accepted h2")
return
end

if string.find(stderr_data, "ALPN: server accepted http/1.1") ~= nil then
ngx.say("alpn server accepted http/1.1")
return
end
if string.find(stderr_data, "ALPN, server accepted to use h2") ~= nil then
ngx.say("alpn server accepted h2")
return
end

if string.find(stderr_data, " ALPN, server accepted to use http/1.1") ~= nil then
ngx.say("alpn server accepted http/1.1")
return
end
}
}
--- request
GET /t
--- response_body
alpn server accepted h2
--- no_error_log
[error]
[alert]
[warn]
[crit]

=== TEST 2: disable http2 alpn
--- http_config
lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;";

server {
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
listen 60000 ssl;
server_name example.com;
ssl_certificate ../../cert/example.com.crt;
ssl_certificate_key ../../cert/example.com.key;
ssl_session_cache off;
ssl_session_tickets on;
server_tokens off;
http2 on;
ssl_client_hello_by_lua_block {
local tls = require("resty.kong.tls")
local ok, err = tls.disable_http2_alpn()
if not ok then
ngx.log(ngx.ERR, "failed to disable http2")
end
}
location /foo {
default_type 'text/plain';
content_by_lua_block {ngx.exit(200)}
more_clear_headers Date;
}
}
--- config
server_tokens off;
location /t {
content_by_lua_block {
local ngx_pipe = require "ngx.pipe"
local proc = ngx_pipe.spawn({'curl', '-vk', '--resolve', 'example.com:60000:127.0.0.1', 'https://example.com:60000'})
local stdout_data, err = proc:stdout_read_all()
if not stdout_data then
ngx.say(err)
return
end

local stderr_data, err = proc:stderr_read_all()
if not stderr_data then
ngx.say(err)
return
end

if string.find(stderr_data, "ALPN: server accepted h2") ~= nil then
ngx.say("alpn server accepted h2")
return
end

if string.find(stderr_data, "ALPN: server accepted http/1.1") ~= nil then
ngx.say("alpn server accepted http/1.1")
return
end

if string.find(stderr_data, "ALPN, server accepted to use h2") ~= nil then
ngx.say("alpn server accepted h2")
return
end

if string.find(stderr_data, " ALPN, server accepted to use http/1.1") ~= nil then
ngx.say("alpn server accepted http/1.1")
return
end
}
}
--- request
GET /t
--- response_body
alpn server accepted http/1.1
--- no_error_log
[error]
[alert]
[warn]
[crit]
Loading