diff --git a/CHANGELOG/unreleased/kong/11529.yaml b/CHANGELOG/unreleased/kong/11529.yaml new file mode 100644 index 000000000000..419d4ec0ab90 --- /dev/null +++ b/CHANGELOG/unreleased/kong/11529.yaml @@ -0,0 +1,7 @@ +message: adds certificate support for service protocol `grpcs`. +type: bugfix +scope: Admin API +prs: + - 11529 +jiras: + - "FTI-5309" diff --git a/kong/clustering/compat/checkers.lua b/kong/clustering/compat/checkers.lua index 7fbfca3cec22..6f313349d4a1 100644 --- a/kong/clustering/compat/checkers.lua +++ b/kong/clustering/compat/checkers.lua @@ -22,7 +22,42 @@ do end + local compatible_checkers = { + { 3005000000, --[[ 3.5.0.0 ]] + function(config_table, dp_version, log_suffix) + -- remove tls_verify, ca_certificates, tls_verify_depth fields for core entity services + local config_services = config_table["services"] + if not config_services then + return nil + end + + local has_update + for _, t in ipairs(config_services) do + if t["protocol"] == "grpcs" then + if t["tls_verify"] or t["tls_verify_depth"] or t["ca_certificates"] + then + t["tls_verify"] = nil + t["tls_verify_depth"] = nil + t["ca_certificates"] = nil + + has_update = true + end + end + end + + if has_update then + log_warn_message("grpcs protocol service contains configuration 'service.tls_verify'" .. + "or 'service.tls_verify_depth' or 'service.ca_certificates'", + "be removed", + dp_version, + log_suffix) + end + + return has_update + end + }, + { 3003000000, --[[ 3.3.0.0 ]] function(config_table, dp_version, log_suffix) local has_update diff --git a/kong/db/schema/entities/services.lua b/kong/db/schema/entities/services.lua index 030eb90c4389..aed77e327c56 100644 --- a/kong/db/schema/entities/services.lua +++ b/kong/db/schema/entities/services.lua @@ -50,23 +50,23 @@ return { entity_checks = { { conditional = { if_field = "protocol", - if_match = { one_of = { "tcp", "tls", "udp", "grpc", "grpcs" }}, + if_match = { one_of = { "tcp", "tls", "udp", "grpc", "grpcs" } }, then_field = "path", then_match = { eq = null }}}, { conditional = { if_field = "protocol", - if_match = { not_one_of = {"https", "tls"} }, + if_match = { not_one_of = { "https", "tls" } }, then_field = "client_certificate", then_match = { eq = null }}}, { conditional = { if_field = "protocol", - if_match = { not_one_of = {"https", "tls"} }, + if_match = { not_one_of = { "https", "tls", "grpcs" } }, then_field = "tls_verify", then_match = { eq = null }}}, { conditional = { if_field = "protocol", - if_match = { not_one_of = {"https", "tls"} }, + if_match = { not_one_of = { "https", "tls", "grpcs" } }, then_field = "tls_verify_depth", then_match = { eq = null }}}, { conditional = { if_field = "protocol", - if_match = { not_one_of = {"https", "tls"} }, + if_match = { not_one_of = { "https", "tls", "grpcs" } }, then_field = "ca_certificates", then_match = { eq = null }}}, }, @@ -96,7 +96,7 @@ return { host = parsed_url.host or null, port = port or parsed_url.port or - (protocol == "http" and 80) or + (protocol == "http" and 80) or (protocol == "https" and 443) or default_port, path = parsed_url.path or null, diff --git a/spec/01-unit/01-db/01-schema/05-services_spec.lua b/spec/01-unit/01-db/01-schema/05-services_spec.lua index 85e823b527b7..f2ec2b4a991f 100644 --- a/spec/01-unit/01-db/01-schema/05-services_spec.lua +++ b/spec/01-unit/01-db/01-schema/05-services_spec.lua @@ -579,6 +579,21 @@ describe("services", function() assert.is_true(ok) end) + it("'protocol' accepts 'grpcs' with tls_verify and ca_certificates", function() + local service = { + protocol = "grpcs", + host = "x.y", + port = 80, + enabled = true, + tls_verify = true, + ca_certificates = { "41f484e9-7888-495d-9283-1d4ce2168172" }, + } + + local ok, err = Services:validate(service) + assert.is_nil(err) + assert.is_true(ok) + end) + it("if 'protocol = tcp/tls/udp/grpc/grpcs', then 'path' is empty", function() for _, v in ipairs({ "tcp", "tls", "udp", "grpc", "grpcs" }) do local service = { diff --git a/spec/02-integration/05-proxy/19-grpc_proxy_spec.lua b/spec/02-integration/05-proxy/19-grpc_proxy_spec.lua index 2add432ae46e..e17d7914c56d 100644 --- a/spec/02-integration/05-proxy/19-grpc_proxy_spec.lua +++ b/spec/02-integration/05-proxy/19-grpc_proxy_spec.lua @@ -2,6 +2,7 @@ local helpers = require "spec.helpers" local cjson = require "cjson" local pl_path = require "pl.path" local atc_compat = require "kong.router.compat" +local ssl_fixtures = require "spec.fixtures.ssl" local FILE_LOG_PATH = os.tmpname() @@ -45,373 +46,471 @@ end for _, flavor in ipairs({ "traditional", "traditional_compatible", "expressions" }) do -for _, strategy in helpers.each_strategy() do - - describe("gRPC Proxying [#" .. strategy .. ", flavor = " .. flavor .. "]", function() - local proxy_client_grpc - local proxy_client_grpcs - local proxy_client - local proxy_client_ssl - local proxy_client_h2c - local proxy_client_h2 - - reload_router(flavor) - - lazy_setup(function() - local bp = helpers.get_db_utils(strategy, { - "routes", - "services", - }) - - local service1 = assert(bp.services:insert { - name = "grpc", - url = helpers.grpcbin_url, - }) - - local service2 = assert(bp.services:insert { - name = "grpcs", - url = helpers.grpcbin_ssl_url, - }) - - local mock_grpc_service = assert(bp.services:insert { - name = "mock_grpc_service", - url = "grpc://localhost:8765", - }) - - local mock_grpc_service_retry = assert(bp.services:insert { - name = "mock_grpc_service_retry", - url = "grpc://grpc_retry", - }) - - local upstream_retry = assert(bp.upstreams:insert { - name = "grpc_retry", - }) - - assert(bp.targets:insert { -- bad target, this one will timeout - upstream = upstream_retry, - target = "127.0.0.1:54321", - }) - - assert(bp.targets:insert { - upstream = upstream_retry, - target = "127.0.0.1:8765", - }) - - assert(bp.routes:insert(gen_route(flavor, { - protocols = { "grpc" }, - hosts = { "grpc" }, - service = service1, - }))) - - assert(bp.routes:insert(gen_route(flavor, { - protocols = { "grpcs" }, - hosts = { "grpcs" }, - service = service2, - }))) - - assert(bp.routes:insert(gen_route(flavor, { - protocols = { "grpc" }, - hosts = { "grpc_authority_1.example" }, - service = mock_grpc_service, - preserve_host = true, - }))) - - assert(bp.routes:insert(gen_route(flavor, { - protocols = { "grpc" }, - hosts = { "grpc_authority_2.example" }, - service = mock_grpc_service, - preserve_host = false, - }))) - - assert(bp.routes:insert(gen_route(flavor, { - protocols = { "grpc" }, - hosts = { "grpc_authority_retry.example" }, - service = mock_grpc_service_retry, - preserve_host = false, - }))) - - assert(bp.plugins:insert { - service = mock_grpc_service_retry, - name = "file-log", - config = { - path = FILE_LOG_PATH, - reopen = true, - }, - }) - - local fixtures = { - http_mock = {} - } - - fixtures.http_mock.my_server_block = [[ - server { - server_name myserver; - listen 8765 http2; - - location ~ / { - content_by_lua_block { - ngx.header.content_type = "application/grpc" - ngx.header.received_host = ngx.req.get_headers()["Host"] - } - } - } - ]] - - assert(helpers.start_kong({ - router_flavor = flavor, - database = strategy, - nginx_conf = "spec/fixtures/custom_nginx.template", - }, nil, nil, fixtures)) - - proxy_client_grpc = helpers.proxy_client_grpc() - proxy_client_grpcs = helpers.proxy_client_grpcs() - proxy_client_h2c = helpers.proxy_client_h2c() - proxy_client_h2 = helpers.proxy_client_h2() - proxy_client = helpers.proxy_client() - proxy_client_ssl = helpers.proxy_ssl_client() - end) - - before_each(function() - os.remove(FILE_LOG_PATH) - end) + for _, strategy in helpers.each_strategy() do + + describe("gRPC Proxying [#" .. strategy .. ", flavor = " .. flavor .. "]", function() + local proxy_client_grpc + local proxy_client_grpcs + local proxy_client + local proxy_client_ssl + local proxy_client_h2c + local proxy_client_h2 + local ca_certificate + local mock_grpcs_service + + reload_router(flavor) + + lazy_setup(function() + local bp = helpers.get_db_utils(strategy, { + "routes", + "services", + "ca_certificates", + }) - lazy_teardown(function() - helpers.stop_kong() - end) + local service1 = assert(bp.services:insert { + name = "grpc", + url = helpers.grpcbin_url, + }) - it("proxies grpc", function() - local ok, resp = assert(proxy_client_grpc({ - service = "hello.HelloService.SayHello", - body = { - greeting = "world!" - }, - opts = { - ["-authority"] = "grpc", - } - })) - assert.truthy(ok) - assert.truthy(resp) - end) + local service2 = assert(bp.services:insert { + name = "grpcs", + url = helpers.grpcbin_ssl_url, + }) - it("proxies grpc, streaming response", function() - local ok, resp = assert(proxy_client_grpc({ - service = "hello.HelloService.LotsOfReplies", - body = { - greeting = "world!" - }, - opts = { - ["-authority"] = "grpc", - } - })) - assert.truthy(ok) - assert.truthy(resp) - end) + local mock_grpc_service = assert(bp.services:insert { + name = "mock_grpc_service", + url = "grpc://localhost:8765", + }) + + mock_grpcs_service = assert(bp.services:insert { + name = "mock_grpcs_service", + url = helpers.grpcbin_ssl_url, + }) - it("proxies grpc, streaming request", function() - local ok, resp = assert(proxy_client_grpc({ - service = "hello.HelloService.LotsOfGreetings", - body = [[ - { "greeting": "world!" } - { "greeting": "people!" } - { "greeting": "y`all!" } - ]], - opts = { - ["-authority"] = "grpc", - } - })) - assert.truthy(ok) - assert.truthy(resp) - end) + local mock_grpc_service_retry = assert(bp.services:insert { + name = "mock_grpc_service_retry", + url = "grpc://grpc_retry", + }) - it("proxies grpc, streaming request/response", function() - local ok, resp = assert(proxy_client_grpc({ - service = "hello.HelloService.BidiHello", - body = [[ - { "greeting": "world!" } - { "greeting": "people!" } - { "greeting": "y`all!" } - ]], - opts = { - ["-authority"] = "grpc", - } - })) - assert.truthy(ok) - assert.truthy(resp) - end) + local upstream_retry = assert(bp.upstreams:insert { + name = "grpc_retry", + }) - it("proxies grpcs", function() - local ok, resp = assert(proxy_client_grpcs({ - service = "hello.HelloService.SayHello", - body = { - greeting = "world!" - }, - opts = { - ["-authority"] = "grpcs", - } - })) - assert.truthy(ok) - assert.truthy(resp) - end) + assert(bp.targets:insert { -- bad target, this one will timeout + upstream = upstream_retry, + target = "127.0.0.1:54321", + }) - it("proxies :authority header if `preserve_host` is set", function() - local _, resp = proxy_client_grpc({ - service = "hello.HelloService.SayHello", - body = { - greeting = "world!" - }, - opts = { - ["-authority"] = "grpc_authority_1.example", - ["-v"] = true, - } - }) + assert(bp.targets:insert { + upstream = upstream_retry, + target = "127.0.0.1:8765", + }) - assert.matches("received%-host: grpc_authority_1.example", resp) - end) + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpc" }, + hosts = { "grpc" }, + service = service1, + }))) + + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpcs" }, + hosts = { "grpcs" }, + service = service2, + }))) + + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpc" }, + hosts = { "grpc_authority_1.example" }, + service = mock_grpc_service, + preserve_host = true, + }))) + + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpc" }, + hosts = { "grpc_authority_2.example" }, + service = mock_grpc_service, + preserve_host = false, + }))) + + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpcs" }, + hosts = { "example.com" }, + service = mock_grpcs_service, + preserve_host = false, + }))) + + assert(bp.routes:insert(gen_route(flavor, { + protocols = { "grpc" }, + hosts = { "grpc_authority_retry.example" }, + service = mock_grpc_service_retry, + preserve_host = false, + }))) + + assert(bp.plugins:insert { + service = mock_grpc_service_retry, + name = "file-log", + config = { + path = FILE_LOG_PATH, + reopen = true, + }, + }) + + ca_certificate = assert(bp.ca_certificates:insert({ + cert = ssl_fixtures.cert_ca2, + })) - it("sets default :authority header if `preserve_host` isn't set", function() - local _, resp = proxy_client_grpc({ - service = "hello.HelloService.SayHello", - body = { - greeting = "world!" - }, - opts = { - ["-authority"] = "grpc_authority_2.example", - ["-v"] = true, + local fixtures = { + http_mock = {} } - }) - assert.matches("received%-host: localhost:8765", resp) - end) + fixtures.http_mock.my_server_block = [[ + server { + server_name myserver; + listen 8765 http2; - it("proxies :authority header on balancer retry", function() - local resp - local file_log_json - helpers.wait_until(function() + location ~ / { + content_by_lua_block { + ngx.header.content_type = "application/grpc" + ngx.header.received_host = ngx.req.get_headers()["Host"] + } + } + } + ]] + + assert(helpers.start_kong({ + router_flavor = flavor, + database = strategy, + nginx_conf = "spec/fixtures/custom_nginx.template", + }, nil, nil, fixtures)) + + proxy_client_grpc = helpers.proxy_client_grpc() + proxy_client_grpcs = helpers.proxy_client_grpcs() + proxy_client_h2c = helpers.proxy_client_h2c() + proxy_client_h2 = helpers.proxy_client_h2() + proxy_client = helpers.proxy_client() + proxy_client_ssl = helpers.proxy_ssl_client() + end) + + before_each(function() os.remove(FILE_LOG_PATH) - local _ - _, resp = proxy_client_grpc({ + end) + + lazy_teardown(function() + helpers.stop_kong() + end) + + it("proxies grpc", function() + local ok, resp = assert(proxy_client_grpc({ service = "hello.HelloService.SayHello", body = { greeting = "world!" }, opts = { - ["-authority"] = "grpc_authority_retry.example", - ["-v"] = true, - } - }) - local f = io.open(FILE_LOG_PATH, 'r') - if not f then - return false - end - - file_log_json = cjson.decode((assert(f:read("*a")))) - f:close() - return pl_path.exists(FILE_LOG_PATH) and pl_path.getsize(FILE_LOG_PATH) > 0 - and #file_log_json.tries >= 2 - end, 5) - - assert.matches("received%-host: 127.0.0.1:8765", resp) - end) - - describe("errors with", function() - it("non-http2 request on grpc route", function() - local res = assert(proxy_client:post("/", { - headers = { - ["Host"] = "grpc", - ["Content-Type"] = "application/grpc" + ["-authority"] = "grpc", } })) - local body = assert.res_status(426, res) - local json = cjson.decode(body) - assert.equal("Please use HTTP2 protocol", json.message) - assert.contains("Upgrade", res.headers.connection) - assert.same("HTTP/2", res.headers["upgrade"]) + assert.truthy(ok) + assert.truthy(resp) end) - it("non-http2 request on grpcs route", function() - local res = assert(proxy_client_ssl:post("/", { - headers = { - ["Host"] = "grpcs", - ["Content-Type"] = "application/grpc" + it("proxies grpc, streaming response", function() + local ok, resp = assert(proxy_client_grpc({ + service = "hello.HelloService.LotsOfReplies", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "grpc", } })) - local body = assert.res_status(426, res) - local json = cjson.decode(body) - assert.equal("Please use HTTP2 protocol", json.message) + assert.truthy(ok) + assert.truthy(resp) end) - it("non-grpc request on grpc route (no content-type)", function() - local body, headers = proxy_client_h2c({ - headers = { - ["method"] = "POST", - [":authority"] = "grpc", + it("proxies grpc, streaming request", function() + local ok, resp = assert(proxy_client_grpc({ + service = "hello.HelloService.LotsOfGreetings", + body = [[ + { "greeting": "world!" } + { "greeting": "people!" } + { "greeting": "y`all!" } + ]], + opts = { + ["-authority"] = "grpc", } - }) - local json = cjson.decode(body) - assert.same("415", headers:get(":status")) - assert.same("Non-gRPC request matched gRPC route", json.message) + })) + assert.truthy(ok) + assert.truthy(resp) end) - it("non-grpc request on grpcs route (no content-type)", function() - local body, headers = proxy_client_h2({ - headers = { - ["method"] = "POST", - [":authority"] = "grpcs", + it("proxies grpc, streaming request/response", function() + local ok, resp = assert(proxy_client_grpc({ + service = "hello.HelloService.BidiHello", + body = [[ + { "greeting": "world!" } + { "greeting": "people!" } + { "greeting": "y`all!" } + ]], + opts = { + ["-authority"] = "grpc", } - }) - local json = cjson.decode(body) - assert.same("415", headers:get(":status")) - assert.same("Non-gRPC request matched gRPC route", json.message) + })) + assert.truthy(ok) + assert.truthy(resp) end) - it("non-grpc request on grpc route (non-grpc content-type)", function() - local body, headers = proxy_client_h2c({ - headers = { - ["method"] = "POST", - ["content-type"] = "application/json", - [":authority"] = "grpc", + it("proxies grpcs", function() + local ok, resp = assert(proxy_client_grpcs({ + service = "hello.HelloService.SayHello", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "grpcs", } - }) - local json = cjson.decode(body) - assert.same("415", headers:get(":status")) - assert.same("Non-gRPC request matched gRPC route", json.message) + })) + assert.truthy(ok) + assert.truthy(resp) end) - it("non-grpc request on grpcs route (non-grpc content-type)", function() - local body, headers = proxy_client_h2({ - headers = { - ["method"] = "POST", - ["content-type"] = "application/json", - [":authority"] = "grpcs", + it("proxies :authority header if `preserve_host` is set", function() + local _, resp = proxy_client_grpc({ + service = "hello.HelloService.SayHello", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "grpc_authority_1.example", + ["-v"] = true, } }) - local json = cjson.decode(body) - assert.same("415", headers:get(":status")) - assert.same("Non-gRPC request matched gRPC route", json.message) + + assert.matches("received%-host: grpc_authority_1.example", resp) end) - it("grpc on grpcs route", function() - local ok, resp = proxy_client_grpc({ + it("sets default :authority header if `preserve_host` isn't set", function() + local _, resp = proxy_client_grpc({ service = "hello.HelloService.SayHello", body = { greeting = "world!" }, opts = { - ["-authority"] = "grpcs", + ["-authority"] = "grpc_authority_2.example", + ["-v"] = true, } }) - assert.falsy(ok) - if flavor == "expressions" then - assert.matches("Code: NotFound", resp, nil, true) - assert.matches("Message: NotFound", resp, nil, true) + assert.matches("received%-host: localhost:8765", resp) + end) - else - assert.matches("Code: Canceled", resp, nil, true) - assert.matches("Message: gRPC request matched gRPCs route", resp, nil, true) - end + it("proxies :authority header on balancer retry", function() + local resp + local file_log_json + helpers.wait_until(function() + os.remove(FILE_LOG_PATH) + local _ + _, resp = proxy_client_grpc({ + service = "hello.HelloService.SayHello", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "grpc_authority_retry.example", + ["-v"] = true, + } + }) + local f = io.open(FILE_LOG_PATH, 'r') + if not f then + return false + end + + file_log_json = cjson.decode((assert(f:read("*a")))) + f:close() + return pl_path.exists(FILE_LOG_PATH) and pl_path.getsize(FILE_LOG_PATH) > 0 + and #file_log_json.tries >= 2 + end, 5) + + assert.matches("received%-host: 127.0.0.1:8765", resp) end) + + describe("errors with", function() + it("non-http2 request on grpc route", function() + local res = assert(proxy_client:post("/", { + headers = { + ["Host"] = "grpc", + ["Content-Type"] = "application/grpc" + } + })) + local body = assert.res_status(426, res) + local json = cjson.decode(body) + assert.equal("Please use HTTP2 protocol", json.message) + assert.contains("Upgrade", res.headers.connection) + assert.same("HTTP/2", res.headers["upgrade"]) + end) + + it("non-http2 request on grpcs route", function() + local res = assert(proxy_client_ssl:post("/", { + headers = { + ["Host"] = "grpcs", + ["Content-Type"] = "application/grpc" + } + })) + local body = assert.res_status(426, res) + local json = cjson.decode(body) + assert.equal("Please use HTTP2 protocol", json.message) + end) + + it("non-grpc request on grpc route (no content-type)", function() + local body, headers = proxy_client_h2c({ + headers = { + ["method"] = "POST", + [":authority"] = "grpc", + } + }) + local json = cjson.decode(body) + assert.same("415", headers:get(":status")) + assert.same("Non-gRPC request matched gRPC route", json.message) + end) + + it("non-grpc request on grpcs route (no content-type)", function() + local body, headers = proxy_client_h2({ + headers = { + ["method"] = "POST", + [":authority"] = "grpcs", + } + }) + local json = cjson.decode(body) + assert.same("415", headers:get(":status")) + assert.same("Non-gRPC request matched gRPC route", json.message) + end) + + it("non-grpc request on grpc route (non-grpc content-type)", function() + local body, headers = proxy_client_h2c({ + headers = { + ["method"] = "POST", + ["content-type"] = "application/json", + [":authority"] = "grpc", + } + }) + local json = cjson.decode(body) + assert.same("415", headers:get(":status")) + assert.same("Non-gRPC request matched gRPC route", json.message) + end) + + it("non-grpc request on grpcs route (non-grpc content-type)", function() + local body, headers = proxy_client_h2({ + headers = { + ["method"] = "POST", + ["content-type"] = "application/json", + [":authority"] = "grpcs", + } + }) + local json = cjson.decode(body) + assert.same("415", headers:get(":status")) + assert.same("Non-gRPC request matched gRPC route", json.message) + end) + + it("grpc on grpcs route", function() + local ok, resp = proxy_client_grpc({ + service = "hello.HelloService.SayHello", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "grpcs", + } + }) + assert.falsy(ok) + + if flavor == "expressions" then + assert.matches("Code: NotFound", resp, nil, true) + assert.matches("Message: NotFound", resp, nil, true) + + else + assert.matches("Code: Canceled", resp, nil, true) + assert.matches("Message: gRPC request matched gRPCs route", resp, nil, true) + end + end) + end) + + if strategy ~= "off" then + describe("grpcs with tls", function() + local function proxy_grpcs_with_ca_certificate() + local ok, resp = assert(proxy_client_grpcs({ + service = "hello.HelloService.SayHello", + body = { + greeting = "world!" + }, + opts = { + ["-authority"] = "example.com", + } + })) + assert.truthy(ok) + assert.truthy(resp) + end + + it("proxies grpcs without ca_certificate", function() + proxy_grpcs_with_ca_certificate() + end) + + describe("tls_verify", function() + it("default is off", function() + local admin_client = helpers.admin_client() + local res = assert(admin_client:patch("/services/" .. mock_grpcs_service.id, { + body = { + ca_certificates = { ca_certificate.id, }, + }, + headers = { ["Content-Type"] = "application/json" }, + })) + + assert.res_status(200, res) + proxy_grpcs_with_ca_certificate() + end) + + it("turn it on", function() + local admin_client = helpers.admin_client() + local res = assert(admin_client:patch("/services/" .. mock_grpcs_service.id, { + body = { + tls_verify = true, + ca_certificates = { ca_certificate.id, }, + }, + headers = { ["Content-Type"] = "application/json" }, + })) + + assert.res_status(200, res) + proxy_grpcs_with_ca_certificate() + end) + end) + + describe("tls_verify_depth", function() + lazy_setup(function() + local admin_client = helpers.admin_client() + local res = assert(admin_client:patch("/services/" .. mock_grpcs_service.id, { + body = { + tls_verify = true, + ca_certificates = { ca_certificate.id, }, + }, + headers = { ["Content-Type"] = "application/json" }, + })) + + assert.res_status(200, res) + end) + + it("request is allowed through if depth limit is sufficient", function() + local admin_client = helpers.admin_client() + local res = assert(admin_client:patch("/services/" .. mock_grpcs_service.id, { + body = { + tls_verify_depth = 1, + }, + headers = { ["Content-Type"] = "application/json" }, + })) + + assert.res_status(200, res) + proxy_grpcs_with_ca_certificate() + end) + end) + end) + end end) - end) -end + end end -- flavor