diff --git a/.requirements b/.requirements index 29ba6dea..e54a4b95 100644 --- a/.requirements +++ b/.requirements @@ -1,4 +1,4 @@ -RESTY_VERSION=1.15.8.2 +RESTY_VERSION=1.17.8.2 RESTY_LUAROCKS_VERSION=3.2.1 -RESTY_OPENSSL_VERSION=1.1.1d -RESTY_PCRE_VERSION=8.43 +RESTY_OPENSSL_VERSION=1.1.1h +RESTY_PCRE_VERSION=8.44 diff --git a/LICENSE b/LICENSE index 39ca3d3b..fda79f13 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2016-2019 Kong Inc. + Copyright 2016-2020 Kong Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index cbdbd421..99a7d6e0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Table of Contents * [resty.kong.tls.set\_upstream\_ssl\_trusted\_store](#restykongtlsset_upstream_ssl_trusted_store) * [resty.kong.tls.set\_upstream\_ssl\_verify](#restykongtlsset_upstream_ssl_verify) * [resty.kong.tls.set\_upstream\_ssl\_verify\_depth](#restykongtlsset_upstream_ssl_verify_depth) + * [resty.kong.grpc.set\_authority](#restykonggrpcset_authority) * [resty.kong.tls.disable\_proxy\_ssl](#restykongtlsdisable_proxy_ssl) * [License](#license) @@ -144,7 +145,7 @@ previous ones. [Back to TOC](#table-of-contents) resty.kong.tls.set\_upstream\_ssl\_trusted\_store --------------------------------------------- +------------------------------------------------- **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_trusted\_store(store)* **context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** @@ -206,7 +207,7 @@ end [Back to TOC](#table-of-contents) resty.kong.tls.set\_upstream\_ssl\_verify --------------------------------------------- +----------------------------------------- **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_verify(verify)* **context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** @@ -225,7 +226,7 @@ previous ones. [Back to TOC](#table-of-contents) resty.kong.tls.set\_upstream\_ssl\_verify\_depth --------------------------------------------- +------------------------------------------------ **syntax:** *ok, err = resty.kong.tls.set\_upstream\_ssl\_verify\_depth(depth)* **context:** *rewrite_by_lua*, access_by_lua*, balancer_by_lua** @@ -243,6 +244,38 @@ previous ones. [Back to TOC](#table-of-contents) +resty.kong.grpc.set\_authority +------------------------------ +**syntax:** *ok, err = resty.kong.grpc.set_authority(new_authority)* + +**context:** *rewrite_by_lua*, access_by_lua* + +**subsystems:** *http* + +Overrides the `:authority` pseudo header sent to gRPC upstream by +[ngx\_http\_grpc\_module](https://nginx.org/en/docs/http/ngx_http_grpc_module.html). + +This function is a capability not possible in Nginx through means of config +directive alone. Reason being Nginx auto-generates the `:authority` pseudo header +without giving us a way to override it at config time. Closest being +`grpc_set_header Host "foo.example.com"`, but this will cause the gRPC +module to use the `Host` header and not generate the `:authority` pseudo header, +causing problems for certain gRPC server. + +When called, this function accepts a new value to override the `:authority` +pseudo header that will be generated by the ngx\_http\_grpc\_module for the +current request. + +The `new_authority` parameter **can not** be an empty string. + +On success, this function returns `true`. Otherwise `nil` and a string +describing the error will be returned. + +This function can be called multiple times in the same request. Later calls override +previous ones. + +[Back to TOC](#table-of-contents) + resty.kong.tls.disable\_proxy\_ssl ---------------------------------- **syntax:** *ok, err = resty.kong.tls.disable_proxy_ssl()* @@ -265,7 +298,7 @@ License ======= ``` -Copyright 2019 Kong Inc. +Copyright 2020 Kong Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/lualib/resty/kong/grpc.lua b/lualib/resty/kong/grpc.lua new file mode 100644 index 00000000..b40e79bc --- /dev/null +++ b/lualib/resty/kong/grpc.lua @@ -0,0 +1,78 @@ +-- Copyright 2019-2020 Kong Inc. + +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at + +-- http://www.apache.org/licenses/LICENSE-2.0 + +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + + +local _M = {} + + +local ffi = require("ffi") +local base = require("resty.core.base") +base.allows_subsystem('http') + + +ffi.cdef([[ +int ngx_http_lua_kong_ffi_set_grpc_authority(ngx_http_request_t *r, + const char *buf, size_t buf_len); +]]) + + +local error = error +local type = type +local C = ffi.C +local get_request = base.get_request +local get_phase = ngx.get_phase + + +local NGX_OK = ngx.OK +local NGX_ERROR = ngx.ERROR + + +do + local ALLOWED_PHASES = { + ['rewrite'] = true, + ['access'] = true, + } + + function _M.set_authority(authority) + if not ALLOWED_PHASES[get_phase()] then + error("API disabled in the current context", 2) + end + + if type(authority) ~= "string" then + error("incorrect argument, expects a string, got " .. + type(authority), 2) + end + + if authority == "" then + error("incorrect argument, the value can not be empty string", 2) + end + + local r = get_request() + + local ret = C.ngx_http_lua_kong_ffi_set_grpc_authority(r, authority, + #authority) + if ret == NGX_OK then + return true + end + + if ret == NGX_ERROR then + return nil, "no memory" + end + + error("unknown return code: " .. tostring(ret)) + end +end + + +return _M diff --git a/lualib/resty/kong/tls.lua b/lualib/resty/kong/tls.lua index 667770ec..39a9e7e7 100644 --- a/lualib/resty/kong/tls.lua +++ b/lualib/resty/kong/tls.lua @@ -1,4 +1,4 @@ --- Copyright 2019 Kong Inc. +-- Copyright 2019-2020 Kong Inc. -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. diff --git a/src/ngx_http_lua_kong_module.c b/src/ngx_http_lua_kong_module.c index c1bc02d4..09c240c8 100644 --- a/src/ngx_http_lua_kong_module.c +++ b/src/ngx_http_lua_kong_module.c @@ -1,5 +1,5 @@ /** - * Copyright 2019 Kong Inc. + * Copyright 2019-2020 Kong Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ static int ngx_http_lua_kong_verify_callback(int ok, X509_STORE_CTX *x509_store); #endif static ngx_int_t ngx_http_lua_kong_init(ngx_conf_t *cf); +static ngx_http_lua_kong_ctx_t *ngx_http_lua_kong_get_module_ctx( + ngx_http_request_t *r); static ngx_http_module_t ngx_http_lua_kong_module_ctx = { @@ -309,6 +311,52 @@ ngx_http_lua_kong_ffi_get_full_client_certificate_chain(ngx_http_request_t *r, } +int +ngx_http_lua_kong_ffi_set_grpc_authority(ngx_http_request_t *r, + const char *buf, size_t buf_len) +{ + u_char *host; + ngx_http_lua_kong_ctx_t *ctx; + + ctx = ngx_http_lua_kong_get_module_ctx(r); + if (ctx == NULL) { + return NGX_ERROR; + } + + host = ngx_palloc(r->pool, buf_len); + if (host == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(host, buf, buf_len); + + ctx->grpc_authority.data = host; + ctx->grpc_authority.len = buf_len; + + return NGX_OK; +} + + +void +ngx_http_lua_kong_set_grpc_authority(ngx_http_request_t *r, + ngx_str_t *host) +{ + ngx_http_lua_kong_ctx_t *ctx; + + ctx = ngx_http_get_module_ctx(r, ngx_http_lua_kong_module); + if (ctx == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "skip overriding gRPC authority pseudo-header, " + "module ctx not set"); + return; + } + + if (ctx->grpc_authority.data != NULL) { + *host = ctx->grpc_authority; + } +} + + #if (NGX_HTTP_SSL) /* @@ -623,4 +671,4 @@ ngx_http_lua_kong_get_upstream_ssl_verify(ngx_http_request_t *r, return ctx->upstream_ssl_verify; } -#endif +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_kong_module.h b/src/ngx_http_lua_kong_module.h index 2e4e88c5..fe56586d 100644 --- a/src/ngx_http_lua_kong_module.h +++ b/src/ngx_http_lua_kong_module.h @@ -12,6 +12,7 @@ typedef struct { EVP_PKEY *upstream_client_private_key; X509_STORE *upstream_trusted_store; ngx_uint_t upstream_ssl_verify_depth; + ngx_str_t grpc_authority; unsigned upstream_ssl_verify:1; unsigned upstream_ssl_verify_set:1; unsigned upstream_ssl_verify_depth_set:1; @@ -20,6 +21,8 @@ typedef struct { void ngx_http_lua_kong_set_upstream_ssl(ngx_http_request_t *r, ngx_connection_t *c); +void ngx_http_lua_kong_set_grpc_authority(ngx_http_request_t *r, + ngx_str_t *host); ngx_flag_t ngx_http_lua_kong_get_upstream_ssl_verify(ngx_http_request_t *r, diff --git a/stream/src/ngx_stream_lua_kong_module.c b/stream/src/ngx_stream_lua_kong_module.c index 2da69f8d..4325407e 100644 --- a/stream/src/ngx_stream_lua_kong_module.c +++ b/stream/src/ngx_stream_lua_kong_module.c @@ -1,5 +1,5 @@ /** - * Copyright 2019 Kong Inc. + * Copyright 2019-2020 Kong Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/t/003-grpc.t b/t/003-grpc.t new file mode 100644 index 00000000..842f4b6a --- /dev/null +++ b/t/003-grpc.t @@ -0,0 +1,227 @@ +# 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() * 5); + +my $pwd = cwd(); + +$ENV{TEST_NGINX_HTML_DIR} ||= html_dir(); + +no_long_string(); +#no_diff(); + +run_tests(); + +__DATA__ + +=== TEST 1: not overriding gRPC :authority pseudo-header, uses "localhost" which is set by default for Unix sockets +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock http2; + server_name example.com; + + server_tokens off; + + location / { + default_type 'text/plain'; + more_clear_headers Date; + echo ':authority: $http_host'; + } + } +--- config + server_tokens off; + + location /t { + grpc_pass unix:/$TEST_NGINX_HTML_DIR/nginx.sock; + } + +--- request +GET /t +--- response_body +:authority: localhost + +--- error_code: 200 +--- no_error_log +[error] +[crit] +[alert] + + + +=== TEST 2: overriding gRPC :authority pseudo-header +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock http2; + server_name example.com; + + server_tokens off; + + location / { + default_type 'text/plain'; + more_clear_headers Date; + echo ':authority: $http_host'; + } + } +--- config + server_tokens off; + + location /t { + access_by_lua_block { + local grpc = require("resty.kong.grpc") + + assert(grpc.set_authority("this.is.my.new.authority.example.com")) + } + + grpc_pass unix:/$TEST_NGINX_HTML_DIR/nginx.sock; + } + +--- request +GET /t +--- response_body +:authority: this.is.my.new.authority.example.com + +--- error_code: 200 +--- no_error_log +[error] +[crit] +[alert] + + + +=== TEST 3: when "Host" is set, overriding gRPC :authority pseudo-header does not have effect because :authority is no longer sent +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock http2; + server_name example.com; + + server_tokens off; + + location / { + default_type 'text/plain'; + more_clear_headers Date; + echo ':authority: $http_host'; + } + } +--- config + server_tokens off; + + location /t { + access_by_lua_block { + local grpc = require("resty.kong.grpc") + + assert(grpc.set_authority("this.is.my.new.authority.example.com")) + } + + grpc_set_header Host "this.is.overriding.authority.example.com"; + grpc_pass unix:/$TEST_NGINX_HTML_DIR/nginx.sock; + } + +--- request +GET /t +--- response_body +:authority: this.is.overriding.authority.example.com + +--- error_code: 200 +--- no_error_log +[error] +[crit] +[alert] + + + +=== TEST 4: calling grpc.set_authority multiple times overrides previously set value +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock http2; + server_name example.com; + + server_tokens off; + + location / { + default_type 'text/plain'; + more_clear_headers Date; + echo ':authority: $http_host'; + } + } +--- config + server_tokens off; + + location /t { + access_by_lua_block { + local grpc = require("resty.kong.grpc") + + assert(grpc.set_authority("this.is.my.new.authority.example.com")) + assert(grpc.set_authority("foo.this.is.my.new.authority.example.com")) + } + + grpc_pass unix:/$TEST_NGINX_HTML_DIR/nginx.sock; + } + +--- request +GET /t +--- response_body +:authority: foo.this.is.my.new.authority.example.com + +--- error_code: 200 +--- no_error_log +[error] +[crit] +[alert] + + + +=== TEST 5: grpc.set_authority rejects invalid argument +--- http_config + lua_package_path "../lua-resty-core/lib/?.lua;lualib/?.lua;;"; + + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock http2; + server_name example.com; + + server_tokens off; + + location / { + default_type 'text/plain'; + more_clear_headers Date; + echo ':authority: $http_host'; + } + } +--- config + server_tokens off; + + location /t { + access_by_lua_block { + local grpc = require("resty.kong.grpc") + + ngx.say(pcall(grpc.set_authority, nil)) + ngx.say(pcall(grpc.set_authority, "")) + ngx.say(pcall(grpc.set_authority, 123)) + } + + grpc_pass unix:/$TEST_NGINX_HTML_DIR/nginx.sock; + } + +--- request +GET /t +--- response_body +falseincorrect argument, expects a string, got nil +falseincorrect argument, the value can not be empty string +falseincorrect argument, expects a string, got number + +--- error_code: 200 +--- no_error_log +[error] +[crit] +[alert]