kong/spec/03-plugins/17-ip-restriction/02-access_spec.lua (932 lines of code) (raw):

local helpers = require "spec.helpers" local cjson = require "cjson" for _, strategy in helpers.each_strategy() do describe("Plugin: ip-restriction (access) [#" .. strategy .. "]", function() local plugin local proxy_client local admin_client local db lazy_setup(function() local bp bp, db = helpers.get_db_utils(strategy, { "routes", "services", "plugins", }) local route1 = bp.routes:insert { hosts = { "ip-restriction1.com" }, } local route2 = bp.routes:insert { hosts = { "ip-restriction2.com" }, } local route3 = bp.routes:insert { hosts = { "ip-restriction3.com" }, } local route4 = bp.routes:insert { hosts = { "ip-restriction4.com" }, } local route5 = bp.routes:insert { hosts = { "ip-restriction5.com" }, } local route6 = bp.routes:insert { hosts = { "ip-restriction6.com" }, } local route7 = bp.routes:insert { hosts = { "ip-restriction7.com" }, } local route8 = bp.routes:insert { hosts = { "ip-restriction8.com" }, } local route9 = bp.routes:insert { hosts = { "ip-restriction9.com" }, } local route10 = bp.routes:insert { hosts = { "ip-restriction10.com" }, } local route11 = bp.routes:insert { hosts = { "ip-restriction11.com" }, } local route12 = bp.routes:insert { hosts = { "ip-restriction12.com" }, } local grpc_service = bp.services:insert { name = "grpc1", url = helpers.grpcbin_url, } local route_grpc_deny = assert(bp.routes:insert { protocols = { "grpc" }, paths = { "/hello.HelloService/" }, hosts = { "ip-restriction-grpc1.com" }, service = grpc_service, }) local route_grpc_allow = assert(bp.routes:insert { protocols = { "grpc" }, paths = { "/hello.HelloService/" }, hosts = { "ip-restriction-grpc2.com" }, service = grpc_service, }) local route_grpc_xforwarded_deny = assert(bp.routes:insert { protocols = { "grpc" }, paths = { "/hello.HelloService/" }, hosts = { "ip-restriction-grpc3.com" }, service = grpc_service, }) bp.plugins:insert { name = "ip-restriction", route = { id = route1.id }, config = { deny = { "127.0.0.1", "127.0.0.2" } }, } plugin = assert(db.plugins:insert { name = "ip-restriction", route = { id = route2.id }, config = { deny = { "127.0.0.2" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route3.id }, config = { allow = { "127.0.0.2" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route4.id }, config = { allow = { "127.0.0.1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route5.id }, config = { deny = { "127.0.0.0/24" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route6.id }, config = { allow = { "127.0.0.4" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route7.id }, config = { deny = { "127.0.0.4" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route8.id }, config = { allow = { "0.0.0.0/0" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route9.id }, config = { allow = { "127.0.0.1" }, deny = { "127.0.0.1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route10.id }, config = { allow = { "127.0.0.0/24" }, deny = { "127.0.0.1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route11.id }, config = { allow = { "127.0.0.0/24" }, deny = { "127.0.0.0/24" }, }, }) bp.plugins:insert { name = "ip-restriction", route = { id = route12.id }, config = { deny = { "127.0.0.1", "127.0.0.2" }, status = 401, message = "Forbidden" }, } assert(db.plugins:insert { name = "ip-restriction", route = { id = route_grpc_deny.id }, config = { deny = { "127.0.0.1", "127.0.0.2" } }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route_grpc_allow.id }, config = { deny = { "127.0.0.2" } }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route_grpc_xforwarded_deny.id }, config = { allow = { "127.0.0.4" }, }, }) assert(helpers.start_kong { database = strategy, real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "0.0.0.0/0, ::/0", nginx_conf = "spec/fixtures/custom_nginx.template", }) proxy_client = helpers.proxy_client() admin_client = helpers.admin_client() end) lazy_teardown(function() if proxy_client and admin_client then proxy_client:close() admin_client:close() end helpers.stop_kong() end) describe("deny", function() it("blocks a request when the IP is denied", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction1.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("blocks a request when the IP is denied with status/message", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction12.com" } }) local body = assert.res_status(401, res) local json = cjson.decode(body) assert.same({ message = "Forbidden" }, json) end) it("blocks a request when the IP is denied #grpc", function() local ok, err = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-authority"] = "ip-restriction-grpc1.com", ["-v"] = true, }, } assert.falsy(ok) assert.matches("Code: PermissionDenied", err) end) it("allows a request when the IP is not denied", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("127.0.0.1", json.vars.remote_addr) end) it("allows a request when the IP is not denied #grpc", function() local ok = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-authority"] = "ip-restriction-grpc2.com", ["-v"] = true, }, } assert.truthy(ok) end) it("blocks IP with CIDR", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction5.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("blocks an IP on a allowed CIDR range", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction10.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("takes precedence over an allowed IP", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction9.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("takes precedence over an allowed CIDR range", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction11.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) describe("X-Forwarded-For", function() it("allows without any X-Forwarded-For and allowed IP", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction7.com" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("127.0.0.1", json.vars.remote_addr) end) it("allows with allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction7.com", ["X-Forwarded-For"] = "127.0.0.3" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("127.0.0.3", json.vars.remote_addr) end) it("blocks with not allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction7.com", ["X-Forwarded-For"] = "127.0.0.4" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) end) end) describe("allow", function() it("blocks a request when the IP is not allowed", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction3.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("allows a allowed IP", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction4.com" } }) assert.res_status(200, res) end) describe("X-Forwarded-For", function() it("blocks without any X-Forwarded-For and not allowed IP", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction6.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("block with not allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction6.com", ["X-Forwarded-For"] = "127.0.0.3" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("block with not allowed X-Forwarded-For header #grpc", function() local ok, err = helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-authority"] = "ip-restriction-grpc3.com", ["-v"] = true, }, } assert.falsy(ok) assert.matches("Code: PermissionDenied", err) end) it("allows with allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction6.com", ["X-Forwarded-For"] = "127.0.0.4" } }) assert.res_status(200, res) end) it("allows with allowed X-Forwarded-For header #grpc", function() assert.truthy(helpers.proxy_client_grpc(){ service = "hello.HelloService.SayHello", opts = { ["-authority"] = "ip-restriction-grpc3.com", ["-v"] = true, ["-H"] = "'X-Forwarded-For: 127.0.0.4'", }, }) end) it("allows with allowed complex X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction6.com", ["X-Forwarded-For"] = "127.0.0.4, 127.0.0.3" } }) assert.res_status(200, res) end) end) end) it("supports config changes without restarting", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com" } }) assert.res_status(200, res) res = assert(admin_client:send { method = "PATCH", path = "/plugins/" .. plugin.id, body = { config = { deny = { "127.0.0.1", "127.0.0.2" } }, }, headers = { ["Content-Type"] = "application/json" } }) assert.res_status(200, res) local cache_key = db.plugins:cache_key(plugin) helpers.wait_for_invalidation(cache_key) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) describe("#regression", function() it("handles a CIDR entry with 0.0.0.0/0", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction8.com" } }) assert.res_status(200, res) end) end) end) describe("Plugin: ip-restriction (access) [#" .. strategy .. "]", function() local plugin local proxy_client local admin_client local db lazy_setup(function() local bp bp, db = helpers.get_db_utils(strategy, { "routes", "services", "plugins", }) local route1 = bp.routes:insert { hosts = { "ip-restriction1.com" }, } local route2 = bp.routes:insert { hosts = { "ip-restriction2.com" }, } local route3 = bp.routes:insert { hosts = { "ip-restriction3.com" }, } local route4 = bp.routes:insert { hosts = { "ip-restriction4.com" }, } local route5 = bp.routes:insert { hosts = { "ip-restriction5.com" }, } local route6 = bp.routes:insert { hosts = { "ip-restriction6.com" }, } local route7 = bp.routes:insert { hosts = { "ip-restriction7.com" }, } local route8 = bp.routes:insert { hosts = { "ip-restriction8.com" }, } local route9 = bp.routes:insert { hosts = { "ip-restriction9.com" }, } bp.plugins:insert { name = "ip-restriction", route = { id = route1.id }, config = { deny = { "::1", "::2" } }, } plugin = assert(db.plugins:insert { name = "ip-restriction", route = { id = route2.id }, config = { deny = { "::2" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route3.id }, config = { deny = { "fe80::/8" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route4.id }, config = { allow = { "::2" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route5.id }, config = { allow = { "::1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route6.id }, config = { allow = { "::/0" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route7.id }, config = { allow = { "::1" }, deny = { "::1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route8.id }, config = { allow = { "::1/128" }, deny = { "::1" }, }, }) assert(db.plugins:insert { name = "ip-restriction", route = { id = route9.id }, config = { allow = { "::1/128" }, deny = { "::1/128" }, }, }) assert(helpers.start_kong { database = strategy, real_ip_recursive = "on", trusted_ips = "0.0.0.0/0, ::/0", nginx_conf = "spec/fixtures/custom_nginx.template", }) proxy_client = helpers.proxy_client() admin_client = helpers.admin_client() end) lazy_teardown(function() if proxy_client and admin_client then proxy_client:close() admin_client:close() end helpers.stop_kong() end) describe("deny", function() it("blocks a request when the IPv6 is denied", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction1.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("allows a request when the IPv6 is not denied", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::1", json.vars.remote_addr) end) it("blocks the IPv6 with CIDR", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction3.com", ["X-Real-IP"] = "fe80::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("blocks an IPv6 on a allowed IPv6 CIDR range", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction8.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("takes precedence over an allowed IPv6", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction7.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("takes precedence over an allowed IPv6 CIDR range", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction9.com" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) end) describe("allow", function() it("blocks a request when the IPv6 is not allowed", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction4.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("allows a allowed IPv6", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction5.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::1", json.vars.remote_addr) end) end) it("supports config changes without restarting", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::1", json.vars.remote_addr) res = assert(admin_client:send { method = "PATCH", path = "/plugins/" .. plugin.id, body = { config = { deny = { "::1", "::2" } }, }, headers = { ["Content-Type"] = "application/json" } }) assert.res_status(200, res) local cache_key = db.plugins:cache_key(plugin) helpers.wait_until(function() res = assert(admin_client:send { method = "GET", path = "/cache/" .. cache_key }) res:read_body() return res.status ~= 200 end) local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction2.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) describe("#regression", function() it("handles a CIDR entry with ::/0", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction6.com", ["X-Real-IP"] = "::1", } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::1", json.vars.remote_addr) end) end) end) describe("Plugin: ip-restriction (access) [#" .. strategy .. "]", function() local proxy_client local admin_client lazy_setup(function() local bp bp = helpers.get_db_utils(strategy, { "routes", "services", "plugins", }) local route1 = bp.routes:insert { hosts = { "ip-restriction1.com" }, } local route2 = bp.routes:insert { hosts = { "ip-restriction2.com" }, } bp.plugins:insert { name = "ip-restriction", route = { id = route1.id }, config = { deny = { "::4" } }, } bp.plugins:insert { name = "ip-restriction", route = { id = route2.id }, config = { allow = { "::4" } }, } assert(helpers.start_kong { database = strategy, real_ip_header = "X-Forwarded-For", real_ip_recursive = "on", trusted_ips = "0.0.0.0/0, ::/0", nginx_conf = "spec/fixtures/custom_nginx.template", }) proxy_client = helpers.proxy_client() admin_client = helpers.admin_client() end) lazy_teardown(function() if proxy_client and admin_client then proxy_client:close() admin_client:close() end helpers.stop_kong() end) describe("deny", function() it("allows with allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/request", headers = { ["Host"] = "ip-restriction1.com", ["X-Forwarded-For"] = "::3", } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::3", json.vars.remote_addr) end) it("blocks with not allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction1.com", ["X-Forwarded-For"] = "::4" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("blocks with blocked complex X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction1.com", ["X-Forwarded-For"] = "::4, ::3" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("allows with allowed complex X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction1.com", ["X-Forwarded-For"] = "::3, ::4" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::3", json.vars.remote_addr) end) end) describe("allow", function() it("block with not allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction2.com", ["X-Forwarded-For"] = "::3" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) it("allows with allowed X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction2.com", ["X-Forwarded-For"] = "::4" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::4", json.vars.remote_addr) end) it("allows with allowed complex X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction2.com", ["X-Forwarded-For"] = "::4, ::3" } }) local body = assert.res_status(200, res) local json = cjson.decode(body) assert.equal("::4", json.vars.remote_addr) end) it("blocks with blocked complex X-Forwarded-For header", function() local res = assert(proxy_client:send { method = "GET", path = "/status/200", headers = { ["Host"] = "ip-restriction2.com", ["X-Forwarded-For"] = "::3, ::4" } }) local body = assert.res_status(403, res) local json = cjson.decode(body) assert.same({ message = "Your IP address is not allowed" }, json) end) end) end) end