Merge branch 'master' into xff

This commit is contained in:
Dario Nieuwenhuis 2018-08-16 18:15:14 +02:00 committed by GitHub
commit b5bcb93a4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1532 changed files with 65966 additions and 34963 deletions

View file

@ -22,14 +22,29 @@ RUN clean-install \
diffutils \
dumb-init
# Create symlinks to redirect nginx logs to stdout and stderr docker log collector
# This only works if nginx is started with CMD or ENTRYPOINT
RUN mkdir -p /var/log/nginx \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
COPY . /
ENTRYPOINT ["/usr/bin/dumb-init"]
# Fix permission during the build to avoid issues at runtime
# with volumes (custom templates)
RUN bash -eu -c ' \
writeDirs=( \
/etc/nginx/template \
/etc/ingress-controller/ssl \
/etc/ingress-controller/auth \
/var/log \
/var/log/nginx \
/tmp \
); \
for dir in "${writeDirs[@]}"; do \
mkdir -p ${dir}; \
chown -R www-data.www-data ${dir}; \
done' \
&& chown www-data.www-data /etc/nginx/nginx.conf \
&& chown www-data.www-data /etc/nginx/opentracing.json
# Create symlinks to redirect nginx logs to stdout and stderr docker log collector
# This only works if nginx is started with CMD or ENTRYPOINT
RUN ln -sf /dev/stdout /var/log/nginx/access.log
RUN ln -sf /dev/stderr /var/log/nginx/error.log
CMD ["/nginx-ingress-controller"]

View file

@ -1,5 +1,7 @@
local ngx_balancer = require("ngx.balancer")
local json = require("cjson")
local util = require("util")
local dns_util = require("util.dns")
local configuration = require("configuration")
local round_robin = require("balancer.round_robin")
local chash = require("balancer.chash")
@ -40,6 +42,19 @@ local function get_implementation(backend)
return implementation
end
local function resolve_external_names(original_backend)
local backend = util.deepcopy(original_backend)
local endpoints = {}
for _, endpoint in ipairs(backend.endpoints) do
local ips = dns_util.resolve(endpoint.address)
for _, ip in ipairs(ips) do
table.insert(endpoints, { address = ip, port = endpoint.port })
end
end
backend.endpoints = endpoints
return backend
end
local function sync_backend(backend)
local implementation = get_implementation(backend)
local balancer = balancers[backend.name]
@ -59,6 +74,11 @@ local function sync_backend(backend)
return
end
local service_type = backend.service and backend.service.spec and backend.service.spec["type"]
if service_type == "ExternalName" then
backend = resolve_external_names(backend)
end
balancer:sync(backend)
end

View file

@ -1,6 +1,7 @@
local balancer_resty = require("balancer.resty")
local resty_chash = require("resty.chash")
local util = require("util")
local split = require("util.split")
local _M = balancer_resty:new({ factory = resty_chash, name = "chash" })
@ -15,7 +16,7 @@ end
function _M.balance(self)
local key = util.lua_ngx_var(self.hash_by)
local endpoint_string = self.instance:find(key)
return util.split_pair(endpoint_string, ":")
return split.split_pair(endpoint_string, ":")
end
return _M

View file

@ -7,6 +7,7 @@
local resty_lock = require("resty.lock")
local util = require("util")
local split = require("util.split")
local DECAY_TIME = 10 -- this value is in seconds
local LOCK_KEY = ":ewma_key"
@ -131,10 +132,10 @@ function _M.balance(self)
end
function _M.after_balance(_)
local response_time = tonumber(util.get_first_value(ngx.var.upstream_response_time)) or 0
local connect_time = tonumber(util.get_first_value(ngx.var.upstream_connect_time)) or 0
local response_time = tonumber(split.get_first_value(ngx.var.upstream_response_time)) or 0
local connect_time = tonumber(split.get_first_value(ngx.var.upstream_connect_time)) or 0
local rtt = connect_time + response_time
local upstream = util.get_first_value(ngx.var.upstream_addr)
local upstream = split.get_first_value(ngx.var.upstream_addr)
if util.is_blank(upstream) then
return

View file

@ -1,6 +1,7 @@
local balancer_resty = require("balancer.resty")
local resty_roundrobin = require("resty.roundrobin")
local util = require("util")
local split = require("util.split")
local _M = balancer_resty:new({ factory = resty_roundrobin, name = "round_robin" })
@ -14,7 +15,7 @@ end
function _M.balance(self)
local endpoint_string = self.instance:find()
return util.split_pair(endpoint_string, ":")
return split.split_pair(endpoint_string, ":")
end
return _M

View file

@ -1,6 +1,7 @@
local balancer_resty = require("balancer.resty")
local resty_chash = require("resty.chash")
local util = require("util")
local split = require("util.split")
local ck = require("resty.cookie")
local _M = balancer_resty:new({ factory = resty_chash, name = "sticky" })
@ -74,7 +75,7 @@ end
function _M.balance(self)
local endpoint_string = sticky_endpoint_string(self)
return util.split_pair(endpoint_string, ":")
return split.split_pair(endpoint_string, ":")
end
return _M

View file

@ -1,7 +1,12 @@
local json = require("cjson")
-- this is the Lua representation of Configuration struct in internal/ingress/types.go
local configuration_data = ngx.shared.configuration_data
local certificate_data = ngx.shared.certificate_data
local _M = {}
local _M = {
nameservers = {}
}
function _M.get_backends_data()
return configuration_data:get("backends")
@ -27,6 +32,55 @@ local function fetch_request_body()
return body
end
function _M.get_pem_cert_key(hostname)
return certificate_data:get(hostname)
end
local function handle_servers()
if ngx.var.request_method ~= "POST" then
ngx.status = ngx.HTTP_BAD_REQUEST
ngx.print("Only POST requests are allowed!")
return
end
local raw_servers = fetch_request_body()
local ok, servers = pcall(json.decode, raw_servers)
if not ok then
ngx.log(ngx.ERR, "could not parse servers: " .. tostring(servers))
ngx.status = ngx.HTTP_BAD_REQUEST
return
end
local err_buf = {}
for _, server in ipairs(servers) do
if server.hostname and server.sslCert.pemCertKey then
local success, err = certificate_data:safe_set(server.hostname, server.sslCert.pemCertKey)
if not success then
if err == "no memory" then
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
ngx.log(ngx.ERR, "no memory in certificate_data dictionary")
return
end
local err_msg = string.format("error setting certificate for %s: %s\n",
server.hostname, tostring(err))
table.insert(err_buf, err_msg)
end
else
ngx.log(ngx.WARN, "hostname or pemCertKey are not present")
end
end
if #err_buf > 0 then
ngx.log(ngx.ERR, table.concat(err_buf))
ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
return
end
ngx.status = ngx.HTTP_CREATED
end
function _M.call()
if ngx.var.request_method ~= "POST" and ngx.var.request_method ~= "GET" then
ngx.status = ngx.HTTP_BAD_REQUEST
@ -34,6 +88,11 @@ function _M.call()
return
end
if ngx.var.request_uri == "/configuration/servers" then
handle_servers()
return
end
if ngx.var.request_uri ~= "/configuration/backends" then
ngx.status = ngx.HTTP_NOT_FOUND
ngx.print("Not found!")
@ -63,4 +122,8 @@ function _M.call()
ngx.status = ngx.HTTP_CREATED
end
if _TEST then
_M.handle_servers = handle_servers
end
return _M

View file

@ -0,0 +1,50 @@
local socket = ngx.socket.tcp
local cjson = require('cjson')
local defer = require('util.defer')
local assert = assert
local _M = {}
local function send_data(jsonData)
local s = assert(socket())
assert(s:connect('unix:/tmp/prometheus-nginx.socket'))
assert(s:send(jsonData))
assert(s:close())
end
function _M.encode_nginx_stats()
return cjson.encode({
host = ngx.var.host or "-",
method = ngx.var.request_method or "-",
path = ngx.var.location_path or "-",
status = ngx.var.status or "-",
requestLength = tonumber(ngx.var.request_length) or -1,
requestTime = tonumber(ngx.var.request_time) or -1,
responseLength = tonumber(ngx.var.bytes_sent) or -1,
endpoint = ngx.var.upstream_addr or "-",
upstreamLatency = tonumber(ngx.var.upstream_connect_time) or -1,
upstreamResponseTime = tonumber(ngx.var.upstream_response_time) or -1,
upstreamResponseLength = tonumber(ngx.var.upstream_response_length) or -1,
upstreamStatus = ngx.var.upstream_status or "-",
namespace = ngx.var.namespace or "-",
ingress = ngx.var.ingress_name or "-",
service = ngx.var.service_name or "-",
})
end
function _M.call()
local ok, err = defer.to_timer_phase(send_data, _M.encode_nginx_stats())
if not ok then
ngx.log(ngx.ERR, "failed to defer send_data to timer phase: ", err)
return
end
end
return _M

View file

@ -1,5 +1,3 @@
package.path = "./rootfs/etc/nginx/lua/?.lua;./rootfs/etc/nginx/lua/test/mocks/?.lua;" .. package.path
describe("Balancer chash", function()
local balancer_chash = require("balancer.chash")

View file

@ -1,17 +1,5 @@
package.path = "./rootfs/etc/nginx/lua/?.lua;./rootfs/etc/nginx/lua/test/mocks/?.lua;" .. package.path
local util = require("util")
local _ngx = {
shared = {
balancer_ewma = { flush_all = function() end },
balancer_ewma_last_touched_at = { flush_all = function() end }
},
log = function(...) end,
now = function() return os.time() end,
}
_G.ngx = _ngx
describe("Balancer ewma", function()
local balancer_ewma = require("balancer.ewma")

View file

@ -1,12 +1,5 @@
package.path = "./rootfs/etc/nginx/lua/?.lua;./rootfs/etc/nginx/lua/test/mocks/?.lua;" .. package.path
_G._TEST = true
local _ngx = {
shared = {},
log = function(...) end,
}
_G.ngx = _ngx
local balancer, expected_implementations, backends
local function reset_balancer()
@ -84,6 +77,44 @@ describe("Balancer", function()
assert.spy(s).was_called_with(implementation, backend)
end)
it("resolves external name to endpoints when service is of type External name", function()
backend = {
name = "exmaple-com", service = { spec = { ["type"] = "ExternalName" } },
endpoints = {
{ address = "example.com", port = "80", maxFails = 0, failTimeout = 0 }
}
}
local dns_helper = require("test/dns_helper")
dns_helper.mock_dns_query({
{
name = "example.com",
address = "192.168.1.1",
ttl = 3600,
},
{
name = "example.com",
address = "1.2.3.4",
ttl = 60,
}
})
expected_backend = {
name = "exmaple-com", service = { spec = { ["type"] = "ExternalName" } },
endpoints = {
{ address = "192.168.1.1", port = "80" },
{ address = "1.2.3.4", port = "80" },
}
}
local mock_instance = { sync = function(backend) end }
setmetatable(mock_instance, implementation)
implementation.new = function(self, backend) return mock_instance end
assert.has_no.errors(function() balancer.sync_backend(backend) end)
stub(mock_instance, "sync")
assert.has_no.errors(function() balancer.sync_backend(backend) end)
assert.stub(mock_instance.sync).was_called_with(mock_instance, expected_backend)
end)
it("replaces the existing balancer when load balancing config changes for backend", function()
assert.has_no.errors(function() balancer.sync_backend(backend) end)
@ -92,10 +123,10 @@ describe("Balancer", function()
local s_old = spy.on(implementation, "new")
local s = spy.on(new_implementation, "new")
local s_ngx_log = spy.on(_G.ngx, "log")
local s_ngx_log = spy.on(ngx, "log")
assert.has_no.errors(function() balancer.sync_backend(backend) end)
assert.spy(s_ngx_log).was_called_with(ngx.ERR,
assert.spy(s_ngx_log).was_called_with(ngx.INFO,
"LB algorithm changed from round_robin to ewma, resetting the instance")
-- TODO(elvinefendi) figure out why
-- assert.spy(s).was_called_with(new_implementation, backend) does not work here

View file

@ -0,0 +1,262 @@
_G._TEST = true
local cjson = require("cjson")
local configuration = require("configuration")
local unmocked_ngx = _G.ngx
local certificate_data = ngx.shared.certificate_data
function get_backends()
return {
{
name = "my-dummy-backend-1", ["load-balance"] = "sticky",
endpoints = { { address = "10.183.7.40", port = "8080", maxFails = 0, failTimeout = 0 } },
sessionAffinityConfig = { name = "cookie", cookieSessionAffinity = { name = "route", hash = "sha1" } },
},
{
name = "my-dummy-backend-2", ["load-balance"] = "ewma",
endpoints = {
{ address = "10.184.7.40", port = "7070", maxFails = 3, failTimeout = 2 },
{ address = "10.184.7.41", port = "7070", maxFails = 2, failTimeout = 1 },
}
},
{
name = "my-dummy-backend-3", ["load-balance"] = "round_robin",
endpoints = {
{ address = "10.185.7.40", port = "6060", maxFails = 0, failTimeout = 0 },
{ address = "10.185.7.41", port = "6060", maxFails = 2, failTimeout = 1 },
}
},
}
end
function get_mocked_ngx_env()
local _ngx = {
status = ngx.HTTP_OK,
var = {},
req = {
read_body = function() end,
get_body_data = function() return cjson.encode(get_backends()) end,
get_body_file = function() return nil end,
},
log = function(msg) end,
}
setmetatable(_ngx, {__index = _G.ngx})
return _ngx
end
describe("Configuration", function()
before_each(function()
_G.ngx = get_mocked_ngx_env()
end)
after_each(function()
_G.ngx = unmocked_ngx
package.loaded["configuration"] = nil
configuration = require("configuration")
end)
describe("Backends", function()
context("Request method is neither GET nor POST", function()
it("sends 'Only POST and GET requests are allowed!' in the response body", function()
ngx.var.request_method = "PUT"
local s = spy.on(ngx, "print")
assert.has_no.errors(configuration.call)
assert.spy(s).was_called_with("Only POST and GET requests are allowed!")
end)
it("returns a status code of 400", function()
ngx.var.request_method = "PUT"
assert.has_no.errors(configuration.call)
assert.equal(ngx.status, ngx.HTTP_BAD_REQUEST)
end)
end)
context("GET request to /configuration/backends", function()
before_each(function()
ngx.var.request_method = "GET"
ngx.var.request_uri = "/configuration/backends"
end)
it("returns the current configured backends on the response body", function()
-- Encoding backends since comparing tables fail due to reference comparison
local encoded_backends = cjson.encode(get_backends())
ngx.shared.configuration_data:set("backends", encoded_backends)
local s = spy.on(ngx, "print")
assert.has_no.errors(configuration.call)
assert.spy(s).was_called_with(encoded_backends)
end)
it("returns a status of 200", function()
assert.has_no.errors(configuration.call)
assert.equal(ngx.status, ngx.HTTP_OK)
end)
end)
context("POST request to /configuration/backends", function()
before_each(function()
ngx.var.request_method = "POST"
ngx.var.request_uri = "/configuration/backends"
end)
it("stores the posted backends on the shared dictionary", function()
-- Encoding backends since comparing tables fail due to reference comparison
assert.has_no.errors(configuration.call)
assert.equal(ngx.shared.configuration_data:get("backends"), cjson.encode(get_backends()))
end)
context("Failed to read request body", function()
local mocked_get_body_data = ngx.req.get_body_data
before_each(function()
ngx.req.get_body_data = function() return nil end
end)
teardown(function()
ngx.req.get_body_data = mocked_get_body_data
end)
it("returns a status of 400", function()
_G.io.open = function(filename, extension) return false end
assert.has_no.errors(configuration.call)
assert.equal(ngx.status, ngx.HTTP_BAD_REQUEST)
end)
it("logs 'dynamic-configuration: unable to read valid request body to stderr'", function()
local s = spy.on(ngx, "log")
assert.has_no.errors(configuration.call)
assert.spy(s).was_called_with(ngx.ERR, "dynamic-configuration: unable to read valid request body")
end)
end)
context("Failed to set the new backends to the configuration dictionary", function()
local resty_configuration_data_set = ngx.shared.configuration_data.set
before_each(function()
ngx.shared.configuration_data.set = function(key, value) return false, "" end
end)
teardown(function()
ngx.shared.configuration_data.set = resty_configuration_data_set
end)
it("returns a status of 400", function()
assert.has_no.errors(configuration.call)
assert.equal(ngx.status, ngx.HTTP_BAD_REQUEST)
end)
it("logs 'dynamic-configuration: error updating configuration:' to stderr", function()
local s = spy.on(ngx, "log")
assert.has_no.errors(configuration.call)
assert.spy(s).was_called_with(ngx.ERR, "dynamic-configuration: error updating configuration: ")
end)
end)
context("Succeeded to update backends configuration", function()
it("returns a status of 201", function()
assert.has_no.errors(configuration.call)
assert.equal(ngx.status, ngx.HTTP_CREATED)
end)
end)
end)
end)
describe("handle_servers()", function()
it("should not accept non POST methods", function()
ngx.var.request_method = "GET"
local s = spy.on(ngx, "print")
assert.has_no.errors(configuration.handle_servers)
assert.spy(s).was_called_with("Only POST requests are allowed!")
assert.same(ngx.status, ngx.HTTP_BAD_REQUEST)
end)
it("should ignore servers that don't have hostname or pemCertKey set", function()
ngx.var.request_method = "POST"
local mock_servers = cjson.encode({
{
hostname = "hostname",
sslCert = {}
},
{
sslCert = {
pemCertKey = "pemCertKey"
}
}
})
ngx.req.get_body_data = function() return mock_servers end
local s = spy.on(ngx, "log")
assert.has_no.errors(configuration.handle_servers)
assert.spy(s).was_called_with(ngx.WARN, "hostname or pemCertKey are not present")
assert.same(ngx.status, ngx.HTTP_CREATED)
end)
it("should successfully update certificates and keys for each host", function()
ngx.var.request_method = "POST"
local mock_servers = cjson.encode({
{
hostname = "hostname",
sslCert = {
pemCertKey = "pemCertKey"
}
}
})
ngx.req.get_body_data = function() return mock_servers end
assert.has_no.errors(configuration.handle_servers)
assert.same(certificate_data:get("hostname"), "pemCertKey")
assert.same(ngx.status, ngx.HTTP_CREATED)
end)
it("should log an err and set status to Internal Server Error when a certificate cannot be set", function()
ngx.var.request_method = "POST"
ngx.shared.certificate_data.safe_set = function(self, data) return false, "error" end
local mock_servers = cjson.encode({
{
hostname = "hostname",
sslCert = {
pemCertKey = "pemCertKey"
}
},
{
hostname = "hostname2",
sslCert = {
pemCertKey = "pemCertKey2"
}
}
})
ngx.req.get_body_data = function() return mock_servers end
local s = spy.on(ngx, "log")
assert.has_no.errors(configuration.handle_servers)
assert.spy(s).was_called_with(ngx.ERR,
"error setting certificate for hostname: error\nerror setting certificate for hostname2: error\n")
assert.same(ngx.status, ngx.HTTP_INTERNAL_SERVER_ERROR)
end)
it("should log an err, set status to Internal Server Error, and short circuit when shared dictionary is full", function()
ngx.var.request_method = "POST"
ngx.shared.certificate_data.safe_set = function(self, data) return false, "no memory" end
local mock_servers = cjson.encode({
{
hostname = "hostname",
sslCert = {
pemCertKey = "pemCertKey"
}
},
{
hostname = "hostname2",
sslCert = {
pemCertKey = "pemCertKey2"
}
}
})
ngx.req.get_body_data = function() return mock_servers end
local s1 = spy.on(ngx, "log")
local s2 = spy.on(ngx.shared.certificate_data, "safe_set")
assert.has_no.errors(configuration.handle_servers)
assert.spy(s1).was_called_with(ngx.ERR, "no memory in certificate_data dictionary")
assert.spy(s2).was_not_called_with("hostname2", "pemCertKey2")
assert.same(ngx.status, ngx.HTTP_INTERNAL_SERVER_ERROR)
end)
end)
end)

View file

@ -0,0 +1,12 @@
_G._TEST = true
local defer = require("util.defer")
describe("Defer", function()
describe("to_timer_phase", function()
it("executes passed callback immediately if called on timer phase", function()
defer.counter = 0
defer.to_timer_phase(function() defer.counter = defer.counter + 1 end)
assert.equal(defer.counter, 1)
end)
end)
end)

View file

@ -0,0 +1,27 @@
local _M = {}
local configuration = require("configuration")
local resolver = require("resty.dns.resolver")
local old_resolver_new = resolver.new
local function reset(nameservers)
configuration.nameservers = nameservers or { "1.1.1.1" }
end
function _M.mock_new(func, nameservers)
reset(nameservers)
resolver.new = func
end
function _M.mock_dns_query(response, err)
reset()
resolver.new = function(self, options)
local r = old_resolver_new(self, options)
r.query = function(self, name, options, tries)
return response, err
end
return r
end
end
return _M

View file

@ -1 +0,0 @@
return {}

View file

@ -1,3 +0,0 @@
return {
new = function(self, nodes) return {} end
}

View file

@ -1 +0,0 @@
return {}

View file

@ -1,3 +0,0 @@
return {
new = function(self, name, ...) return {} end,
}

View file

@ -1 +0,0 @@
return {}

View file

@ -1,3 +0,0 @@
return {
new = function(self, nodes) return {} end
}

View file

@ -1 +0,0 @@
return {}

View file

@ -1 +0,0 @@
return {}

View file

@ -0,0 +1,96 @@
_G._TEST = true
local cjson = require("cjson")
describe("Monitor", function()
local monitor = require("monitor")
describe("encode_nginx_stats()", function()
it("successfuly encodes the current stats of nginx to JSON", function()
local nginx_environment = {
host = "testshop.com",
status = "200",
bytes_sent = "150",
server_protocol = "HTTP",
request_method = "GET",
location_path = "/admin",
request_length = "300",
request_time = "210",
proxy_upstream_name = "test-upstream",
upstream_addr = "2.2.2.2",
upstream_response_time = "200",
upstream_response_length = "150",
upstream_connect_time = "1",
upstream_status = "220",
namespace = "test-app-production",
ingress_name = "web-yml",
service_name = "test-app",
}
ngx.var = nginx_environment
local encode_nginx_stats = monitor.encode_nginx_stats
local encoded_json_stats = encode_nginx_stats()
local decoded_json_stats = cjson.decode(encoded_json_stats)
local expected_json_stats = {
host = "testshop.com",
status = "200",
responseLength = 150.0,
method = "GET",
path = "/admin",
requestLength = 300.0,
requestTime = 210.0,
endpoint = "2.2.2.2",
upstreamResponseTime = 200,
upstreamStatus = "220",
upstreamLatency = 1.0,
upstreamResponseLength = 150.0,
namespace = "test-app-production",
ingress = "web-yml",
service = "test-app",
}
assert.are.same(decoded_json_stats,expected_json_stats)
end)
it("replaces empty numeric keys with -1 and missing string keys with -", function()
local nginx_environment = {
remote_addr = "10.10.10.10",
realip_remote_addr = "5.5.5.5",
remote_user = "francisco",
server_protocol = "HTTP",
request_method = "GET",
location_path = "/admin",
request_time = "202",
proxy_upstream_name = "test-upstream",
upstream_addr = "2.2.2.2",
upstream_response_time = "201",
upstream_status = "220",
ingress_name = "web-yml",
}
ngx.var = nginx_environment
local encode_nginx_stats = monitor.encode_nginx_stats
local encoded_json_stats = encode_nginx_stats()
local decoded_json_stats = cjson.decode(encoded_json_stats)
local expected_json_stats = {
host = "-",
status = "-",
responseLength = -1,
method = "GET",
path = "/admin",
requestLength = -1,
requestTime = 202.0,
endpoint = "2.2.2.2",
upstreamStatus = "220",
namespace = "-",
ingress = "web-yml",
upstreamLatency = -1,
upstreamResponseTime = 201,
upstreamResponseLength = -1,
responseLength = -1,
service = "-",
}
assert.are.same(decoded_json_stats,expected_json_stats)
end)
end)
end)

View file

@ -0,0 +1,32 @@
local ffi = require("ffi")
-- without this we get errors such as "attempt to redefine XXX"
local old_cdef = ffi.cdef
local exists = {}
ffi.cdef = function(def)
if exists[def] then
return
end
exists[def] = true
return old_cdef(def)
end
local old_udp = ngx.socket.udp
ngx.socket.udp = function(...)
local socket = old_udp(...)
socket.send = function(...)
error("ngx.socket.udp:send please mock this to use in tests")
end
return socket
end
local old_tcp = ngx.socket.tcp
ngx.socket.tcp = function(...)
local socket = old_tcp(...)
socket.send = function(...)
error("ngx.socket.tcp:send please mock this to use in tests")
end
return socket
end
require "busted.runner"({ standalone = false })

View file

@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Copyright 2018 The Kubernetes Authors.
#
# 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.
install()
{
package="$1"
version="$2"
if luarocks list --porcelain $package $version | grep -q "installed"; then
echo $package already installed, skipping ;
else
sudo luarocks install $package $version;
fi
}
install busted 2.0.rc12
install lua-cjson 2.1.0-1

View file

@ -0,0 +1,74 @@
describe("resolve", function()
local dns = require("util.dns")
local dns_helper = require("test/dns_helper")
it("sets correct nameservers", function()
dns_helper.mock_new(function(self, options)
assert.are.same({ nameservers = { "1.2.3.4", "4.5.6.7" }, retrans = 5, timeout = 2000 }, options)
return nil, ""
end, { "1.2.3.4", "4.5.6.7" })
dns.resolve("example.com")
end)
it("returns host when an error happens", function()
local s_ngx_log = spy.on(ngx, "log")
dns_helper.mock_new(function(...) return nil, "an error" end)
assert.are.same({ "example.com" }, dns.resolve("example.com"))
assert.spy(s_ngx_log).was_called_with(ngx.ERR, "failed to instantiate the resolver: an error")
dns_helper.mock_dns_query(nil, "oops!")
assert.are.same({ "example.com" }, dns.resolve("example.com"))
assert.spy(s_ngx_log).was_called_with(ngx.ERR, "failed to query the DNS server: oops!")
dns_helper.mock_dns_query({ errcode = 1, errstr = "format error" })
assert.are.same({ "example.com" }, dns.resolve("example.com"))
assert.spy(s_ngx_log).was_called_with(ngx.ERR, "server returned error code: 1: format error")
dns_helper.mock_dns_query({})
assert.are.same({ "example.com" }, dns.resolve("example.com"))
assert.spy(s_ngx_log).was_called_with(ngx.ERR, "no A record resolved")
dns_helper.mock_dns_query({ { name = "example.com", cname = "sub.example.com", ttl = 60 } })
assert.are.same({ "example.com" }, dns.resolve("example.com"))
assert.spy(s_ngx_log).was_called_with(ngx.ERR, "no A record resolved")
end)
it("resolves all A records of given host, caches them with minimal ttl and returns from cache next time", function()
dns_helper.mock_dns_query({
{
name = "example.com",
address = "192.168.1.1",
ttl = 3600,
},
{
name = "example.com",
address = "1.2.3.4",
ttl = 60,
}
})
local lrucache = require("resty.lrucache")
local old_lrucache_new = lrucache.new
lrucache.new = function(...)
local cache = old_lrucache_new(...)
local old_set = cache.set
cache.set = function(self, key, value, ttl)
assert.equal("example.com", key)
assert.are.same({ "192.168.1.1", "1.2.3.4" }, value)
assert.equal(60, ttl)
return old_set(self, key, value, ttl)
end
return cache
end
assert.are.same({ "192.168.1.1", "1.2.3.4" }, dns.resolve("example.com"))
dns_helper.mock_new(function(...)
error("expected to short-circuit and return response from cache")
end)
assert.are.same({ "192.168.1.1", "1.2.3.4" }, dns.resolve("example.com"))
end)
end)

View file

@ -49,17 +49,6 @@ function _M.lua_ngx_var(ngx_var)
return ngx.var[var_name]
end
function _M.split_pair(pair, seperator)
local i = pair:find(seperator)
if i == nil then
return pair, nil
else
local name = pair:sub(1, i - 1)
local value = pair:sub(i + 1, -1)
return name, value
end
end
-- this implementation is taken from
-- https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3
-- and modified for use in this project
@ -88,30 +77,6 @@ function _M.is_blank(str)
return str == nil or string_len(str) == 0
end
-- http://nginx.org/en/docs/http/ngx_http_upstream_module.html#example
-- CAVEAT: nginx is giving out : instead of , so the docs are wrong
-- 127.0.0.1:26157 : 127.0.0.1:26157 , ngx.var.upstream_addr
-- 200 : 200 , ngx.var.upstream_status
-- 0.00 : 0.00, ngx.var.upstream_response_time
function _M.split_upstream_var(var)
if not var then
return nil, nil
end
local t = {}
for v in var:gmatch("[^%s|,]+") do
if v ~= ":" then
t[#t+1] = v
end
end
return t
end
function _M.get_first_value(var)
local t = _M.split_upstream_var(var) or {}
if #t == 0 then return nil end
return t[1]
end
-- this implementation is taken from:
-- https://github.com/luafun/luafun/blob/master/fun.lua#L33
-- SHA: 04c99f9c393e54a604adde4b25b794f48104e0d0
@ -130,4 +95,13 @@ local function deepcopy(orig)
end
_M.deepcopy = deepcopy
local function tablelength(T)
local count = 0
for _ in pairs(T) do
count = count + 1
end
return count
end
_M.tablelength = tablelength
return _M

View file

@ -0,0 +1,57 @@
local util = require("util")
local timer_started = false
local queue = {}
local MAX_QUEUE_SIZE = 10000
local _M = {}
local function flush_queue(premature)
-- TODO Investigate if we should actually still flush the queue when we're
-- shutting down.
if premature then return end
local current_queue = queue
queue = {}
timer_started = false
for _,v in ipairs(current_queue) do
v.func(unpack(v.args))
end
end
-- `to_timer_phase` will enqueue a function that will be executed in a timer
-- context, at a later point in time. The purpose is that some APIs (such as
-- sockets) are not available during some nginx request phases (such as the
-- logging phase), but are available for use in timers. There are no ordering
-- guarantees for when a function will be executed.
function _M.to_timer_phase(func, ...)
if ngx.get_phase() == "timer" then
func(...)
return true
end
if #queue >= MAX_QUEUE_SIZE then
ngx.log(ngx.ERR, "deferred timer queue full")
return nil, "deferred timer queue full"
end
table.insert(queue, { func = func, args = {...} })
if not timer_started then
local ok, err = ngx.timer.at(0, flush_queue)
if ok then
-- unfortunately this is to deal with tests - when running unit tests, we
-- dont actually run the timer, we call the function inline
if util.tablelength(queue) > 0 then
timer_started = true
end
else
local msg = "failed to create timer: " .. tostring(err)
ngx.log(ngx.ERR, msg)
return nil, msg
end
end
return true
end
return _M

View file

@ -0,0 +1,75 @@
local resolver = require("resty.dns.resolver")
local lrucache = require("resty.lrucache")
local configuration = require("configuration")
local util = require("util")
local _M = {}
local CACHE_SIZE = 10000
local MAXIMUM_TTL_VALUE = 2147483647 -- maximum value according to https://tools.ietf.org/html/rfc2181
local cache, err = lrucache.new(CACHE_SIZE)
if not cache then
return error("failed to create the cache: " .. (err or "unknown"))
end
local function a_records_and_max_ttl(answers)
local addresses = {}
local ttl = MAXIMUM_TTL_VALUE -- maximum value according to https://tools.ietf.org/html/rfc2181
for _, ans in ipairs(answers) do
if ans.address then
table.insert(addresses, ans.address)
if ttl > ans.ttl then
ttl = ans.ttl
end
end
end
return addresses, ttl
end
function _M.resolve(host)
local cached_addresses = cache:get(host)
if cached_addresses then
local message = string.format(
"addresses %s for host %s was resolved from cache",
table.concat(cached_addresses, ", "), host)
ngx.log(ngx.INFO, message)
return cached_addresses
end
local r
r, err = resolver:new{
nameservers = util.deepcopy(configuration.nameservers),
retrans = 5,
timeout = 2000, -- 2 sec
}
if not r then
ngx.log(ngx.ERR, "failed to instantiate the resolver: " .. tostring(err))
return { host }
end
local answers
answers, err = r:query(host, { qtype = r.TYPE_A }, {})
if not answers then
ngx.log(ngx.ERR, "failed to query the DNS server: " .. tostring(err))
return { host }
end
if answers.errcode then
ngx.log(ngx.ERR, string.format("server returned error code: %s: %s", answers.errcode, answers.errstr))
return { host }
end
local addresses, ttl = a_records_and_max_ttl(answers)
if #addresses == 0 then
ngx.log(ngx.ERR, "no A record resolved")
return { host }
end
cache:set(host, addresses, ttl)
return addresses
end
return _M

View file

@ -0,0 +1,70 @@
local _M = {}
-- splits strings into host and port
local function parse_addr(addr)
local _, _, host, port = addr:find("([^:]+):([^:]+)")
if host and port then
return {host=host, port=port}
else
return nil, "error in parsing upstream address!"
end
end
function _M.get_first_value(var)
local t = _M.split_upstream_var(var) or {}
if #t == 0 then return nil end
return t[1]
end
function _M.split_pair(pair, seperator)
local i = pair:find(seperator)
if i == nil then
return pair, nil
else
local name = pair:sub(1, i - 1)
local value = pair:sub(i + 1, -1)
return name, value
end
end
-- http://nginx.org/en/docs/http/ngx_http_upstream_module.html#example
-- CAVEAT: nginx is giving out : instead of , so the docs are wrong
-- 127.0.0.1:26157 : 127.0.0.1:26157 , ngx.var.upstream_addr
-- 200 : 200 , ngx.var.upstream_status
-- 0.00 : 0.00, ngx.var.upstream_response_time
function _M.split_upstream_var(var)
if not var then
return nil, nil
end
local t = {}
for v in var:gmatch("[^%s|,]+") do
if v ~= ":" then
t[#t+1] = v
end
end
return t
end
-- Splits an NGINX $upstream_addr and returns an array of tables with a `host` and `port` key-value pair.
function _M.split_upstream_addr(addrs_str)
if not addrs_str then
return nil, nil
end
local addrs = _M.split_upstream_var(addrs_str)
local host_and_ports = {}
for _, v in ipairs(addrs) do
local a, err = parse_addr(v)
if err then
return nil, err
end
host_and_ports[#host_and_ports+1] = a
end
if #host_and_ports == 0 then
return nil, "no upstream addresses to parse!"
end
return host_and_ports
end
return _M

View file

@ -1,5 +1,5 @@
# A very simple nginx configuration file that forces nginx to start.
pid /run/nginx.pid;
pid /tmp/nginx.pid;
events {}
http {}

View file

@ -0,0 +1 @@
{}

View file

@ -7,11 +7,18 @@
{{ $proxyHeaders := .ProxySetHeaders }}
{{ $addHeaders := .AddHeaders }}
# Configuration checksum: {{ $all.Cfg.Checksum }}
# setup custom paths that do not require root access
pid /tmp/nginx.pid;
{{ if $cfg.EnableModsecurity }}
load_module /etc/nginx/modules/ngx_http_modsecurity_module.so;
{{ end }}
{{ buildOpentracingLoad $cfg }}
{{ if $cfg.EnableOpentracing }}
load_module /etc/nginx/modules/ngx_http_opentracing_module.so;
{{ end }}
daemon off;
@ -20,7 +27,6 @@ worker_processes {{ $cfg.WorkerProcesses }};
worker_cpu_affinity {{ $cfg.WorkerCpuAffinity }};
{{ end }}
pid /run/nginx.pid;
{{ if ne .MaxOpenFiles 0 }}
worker_rlimit_nofile {{ .MaxOpenFiles }};
{{ end }}
@ -30,11 +36,15 @@ worker_rlimit_nofile {{ .MaxOpenFiles }};
worker_shutdown_timeout {{ $cfg.WorkerShutdownTimeout }} ;
events {
multi_accept on;
multi_accept {{ if $cfg.EnableMultiAccept }}on{{ else }}off{{ end }};
worker_connections {{ $cfg.MaxWorkerConnections }};
use epoll;
}
{{ if not (empty $cfg.MainSnippet) }}
{{ $cfg.MainSnippet }}
{{ end }}
http {
{{ if not $all.DisableLua }}
lua_package_cpath "/usr/local/lib/lua/?.so;/usr/lib/lua-platform-path/lua/5.1/?.so;;";
@ -58,6 +68,7 @@ http {
error("require failed: " .. tostring(res))
else
configuration = res
configuration.nameservers = { {{ buildResolversForLua $cfg.Resolver $cfg.DisableIpv6DNS }} }
end
ok, res = pcall(require, "balancer")
@ -67,6 +78,13 @@ http {
balancer = res
end
{{ end }}
ok, res = pcall(require, "monitor")
if not ok then
error("require failed: " .. tostring(res))
else
monitor = res
end
}
{{ if $all.DynamicConfigurationEnabled }}
@ -101,11 +119,6 @@ http {
geoip_proxy_recursive on;
{{ end }}
{{ if $cfg.EnableVtsStatus }}
vhost_traffic_status_zone shared:vhost_traffic_status:{{ $cfg.VtsStatusZoneSize }};
vhost_traffic_status_filter_by_set_key {{ $cfg.VtsDefaultFilterKey }};
{{ end }}
aio threads;
aio_write on;
@ -119,6 +132,11 @@ http {
keepalive_timeout {{ $cfg.KeepAlive }}s;
keepalive_requests {{ $cfg.KeepAliveRequests }};
client_body_temp_path /tmp/client-body;
fastcgi_temp_path /tmp/fastcgi-temp;
proxy_temp_path /tmp/proxy-temp;
ajp_temp_path /tmp/ajp-temp;
client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }};
client_header_timeout {{ $cfg.ClientHeaderTimeout }}s;
large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }};
@ -161,7 +179,7 @@ http {
{{ if $cfg.UseGzip }}
gzip on;
gzip_comp_level 5;
gzip_comp_level {{ $cfg.GzipLevel }};
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types {{ $cfg.GzipTypes }};
@ -176,7 +194,7 @@ http {
server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }};
{{ if not $cfg.ShowServerTokens }}
more_set_headers "Server: ";
more_clear_headers Server;
{{ end }}
# disable warnings
@ -186,6 +204,7 @@ http {
# $namespace
# $ingress_name
# $service_name
# $service_port
log_format upstreaminfo {{ if $cfg.LogFormatEscapeJSON }}escape=json {{ end }}'{{ buildLogFormatUpstream $cfg }}';
{{/* map urls that should not appear in access.log */}}
@ -379,10 +398,10 @@ http {
{{ end }}
{{ if not $all.DynamicConfigurationEnabled }}
{{ range $name, $upstream := $backends }}
{{ range $upstream := $backends }}
{{ if eq $upstream.SessionAffinity.AffinityType "cookie" }}
upstream sticky-{{ $upstream.Name }} {
sticky hash={{ $upstream.SessionAffinity.CookieSessionAffinity.Hash }} name={{ $upstream.SessionAffinity.CookieSessionAffinity.Name }} httponly;
sticky hash={{ $upstream.SessionAffinity.CookieSessionAffinity.Hash }} name={{ $upstream.SessionAffinity.CookieSessionAffinity.Name }}{{if eq (len $upstream.SessionAffinity.CookieSessionAffinity.Locations) 1 }}{{ range $locationName, $locationPaths := $upstream.SessionAffinity.CookieSessionAffinity.Locations }}{{ if eq (len $locationPaths) 1 }} path={{ index $locationPaths 0 }}{{ end }}{{ end }}{{ end }} httponly;
{{ if (gt $cfg.UpstreamKeepaliveConnections 0) }}
keepalive {{ $cfg.UpstreamKeepaliveConnections }};
@ -421,7 +440,7 @@ http {
{{ end }}
{{/* build the maps that will be use to validate the Whitelist */}}
{{ range $index, $server := $servers }}
{{ range $server := $servers }}
{{ range $location := $server.Locations }}
{{ $path := buildLocation $location }}
@ -491,7 +510,7 @@ http {
}
{{ end }}
{{ range $index, $server := $servers }}
{{ range $server := $servers }}
## start server {{ $server.Hostname }}
server {
@ -511,9 +530,6 @@ http {
# default server, used for NGINX healthcheck and access to nginx stats
server {
# Use the port {{ $all.ListenPorts.Status }} (random value just to avoid known ports) as default port for nginx.
# Changing this value requires a change in:
# https://github.com/kubernetes/ingress-nginx/blob/master/controllers/nginx/pkg/cmd/controller/nginx.go
listen {{ $all.ListenPorts.Status }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};
{{ if $IsIPV6Enabled }}listen [::]:{{ $all.ListenPorts.Status }} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }};{{ end }}
set $proxy_upstream_name "-";
@ -551,14 +567,8 @@ http {
opentracing off;
{{ end }}
{{ if $cfg.EnableVtsStatus }}
vhost_traffic_status_display;
vhost_traffic_status_display_format html;
vhost_traffic_status_display_sum_key {{ $cfg.VtsSumKey }};
{{ else }}
access_log off;
stub_status on;
{{ end }}
}
{{ if $all.DynamicConfigurationEnabled }}
@ -612,10 +622,10 @@ stream {
error_log {{ $cfg.ErrorLogPath }};
# TCP services
{{ range $i, $tcpServer := .TCPBackends }}
{{ range $tcpServer := .TCPBackends }}
upstream tcp-{{ $tcpServer.Port }}-{{ $tcpServer.Backend.Namespace }}-{{ $tcpServer.Backend.Name }}-{{ $tcpServer.Backend.Port }} {
{{ range $j, $endpoint := $tcpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ range $endpoint := $tcpServer.Endpoints }}
server {{ $endpoint.Address | formatIP }}:{{ $endpoint.Port }};
{{ end }}
}
server {
@ -641,10 +651,10 @@ stream {
{{ end }}
# UDP services
{{ range $i, $udpServer := .UDPBackends }}
{{ range $udpServer := .UDPBackends }}
upstream udp-{{ $udpServer.Port }}-{{ $udpServer.Backend.Namespace }}-{{ $udpServer.Backend.Name }}-{{ $udpServer.Backend.Port }} {
{{ range $j, $endpoint := $udpServer.Endpoints }}
server {{ $endpoint.Address }}:{{ $endpoint.Port }};
{{ range $endpoint := $udpServer.Endpoints }}
server {{ $endpoint.Address | formatIP }}:{{ $endpoint.Port }};
{{ end }}
}
@ -685,6 +695,9 @@ stream {
proxy_set_header X-Namespace $namespace;
proxy_set_header X-Ingress-Name $ingress_name;
proxy_set_header X-Service-Name $service_name;
proxy_set_header X-Service-Port $service_port;
set $proxy_upstream_name "upstream-default-backend";
rewrite (.*) / break;
@ -739,7 +752,7 @@ stream {
{{/* Listen on {{ $all.ListenPorts.SSLProxy }} because port {{ $all.ListenPorts.HTTPS }} is used in the TLS sni server */}}
{{/* This listener must always have proxy_protocol enabled, because the SNI listener forwards on source IP info in it. */}}
{{ if not (empty $server.SSLCertificate) }}
{{ if not (empty $server.SSLCert.PemFileName) }}
{{ range $address := $all.Cfg.BindAddressIpv4 }}
listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol {{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
{{ else }}
@ -747,17 +760,17 @@ stream {
{{ end }}
{{ if $all.IsIPV6Enabled }}
{{ range $address := $all.Cfg.BindAddressIpv6 }}
{{ if not (empty $server.SSLCertificate) }}listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
{{ if not (empty $server.SSLCert.PemFileName) }}listen {{ $address }}:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
{{ else }}
{{ if not (empty $server.SSLCertificate) }}listen [::]:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
{{ if not (empty $server.SSLCert.PemFileName) }}listen [::]:{{ if $all.IsSSLPassthroughEnabled }}{{ $all.ListenPorts.SSLProxy }} proxy_protocol{{ else }}{{ $all.ListenPorts.HTTPS }}{{ if $all.Cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ end }}{{ end }} {{ if eq $server.Hostname "_"}} default_server {{ if $all.Cfg.ReusePort }}reuseport{{ end }} backlog={{ $all.BacklogSize }}{{end}} ssl {{ if $all.Cfg.UseHTTP2 }}http2{{ end }};
{{ end }}
{{ end }}
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
# PEM sha: {{ $server.SSLPemChecksum }}
ssl_certificate {{ $server.SSLCertificate }};
ssl_certificate_key {{ $server.SSLCertificate }};
{{ if not (empty $server.SSLFullChainCertificate)}}
ssl_trusted_certificate {{ $server.SSLFullChainCertificate }};
# PEM sha: {{ $server.SSLCert.PemSHA }}
ssl_certificate {{ $server.SSLCert.PemFileName }};
ssl_certificate_key {{ $server.SSLCert.PemFileName }};
{{ if not (empty $server.SSLCert.FullChainPemFileName)}}
ssl_trusted_certificate {{ $server.SSLCert.FullChainPemFileName }};
ssl_stapling on;
ssl_stapling_verify on;
{{ end }}
@ -855,6 +868,17 @@ stream {
{{ end }}
location {{ $path }} {
{{ $ing := (getIngressInformation $location.Ingress $location.Path) }}
set $namespace "{{ $ing.Namespace }}";
set $ingress_name "{{ $ing.Rule }}";
set $service_name "{{ $ing.Service }}";
set $service_port "{{ $location.Port }}";
set $location_path "{{ $location.Path }}";
{{ if $all.Cfg.EnableOpentracing }}
opentracing_propagate_context;
{{ end }}
{{ if not $all.DisableLua }}
rewrite_by_lua_block {
{{ if $all.DynamicConfigurationEnabled}}
@ -910,10 +934,12 @@ stream {
{{ if $all.DynamicConfigurationEnabled}}
balancer.log()
{{ end }}
monitor.call()
}
{{ end }}
{{ if (and (not (empty $server.SSLCertificate)) $all.Cfg.HSTS) }}
{{ if (and (not (empty $server.SSLCert.PemFileName)) $all.Cfg.HSTS) }}
if ($scheme = https) {
more_set_headers "Strict-Transport-Security: max-age={{ $all.Cfg.HSTSMaxAge }}{{ if $all.Cfg.HSTSIncludeSubdomains }}; includeSubDomains{{ end }}{{ if $all.Cfg.HSTSPreload }}; preload{{ end }}";
}
@ -930,18 +956,10 @@ stream {
port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }};
{{ if $all.Cfg.EnableVtsStatus }}{{ if $location.VtsFilterKey }} vhost_traffic_status_filter_by_set_key {{ $location.VtsFilterKey }};{{ end }}{{ end }}
set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $all.Backends $location $all.DynamicConfigurationEnabled }}";
{{ $ing := (getIngressInformation $location.Ingress $location.Path) }}
{{/* $ing.Metadata contains the Ingress metadata */}}
set $namespace "{{ $ing.Namespace }}";
set $ingress_name "{{ $ing.Rule }}";
set $service_name "{{ $ing.Service }}";
{{/* redirect to HTTPS can be achieved forcing the redirect or having a SSL Certificate configured for the server */}}
{{ if (or $location.Rewrite.ForceSSLRedirect (and (not (empty $server.SSLCertificate)) $location.Rewrite.SSLRedirect)) }}
{{ if (or $location.Rewrite.ForceSSLRedirect (and (not (empty $server.SSLCert.PemFileName)) $location.Rewrite.SSLRedirect)) }}
{{ if not (isLocationInLocationList $location $all.Cfg.NoTLSRedirectLocations) }}
# enforce ssl on server side
if ($redirect_to_https) {
@ -980,12 +998,13 @@ stream {
auth_request {{ $authPath }};
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
{{- range $idx, $line := buildAuthResponseHeaders $location }}
{{- range $line := buildAuthResponseHeaders $location }}
{{ $line }}
{{- end }}
{{ end }}
{{ if $location.ExternalAuth.SigninURL }}
set_escape_uri $escaped_request_uri $request_uri;
error_page 401 = {{ buildAuthSignURL $location.ExternalAuth.SigninURL }};
{{ end }}
@ -1113,6 +1132,7 @@ stream {
proxy_set_header X-Namespace $namespace;
proxy_set_header X-Ingress-Name $ingress_name;
proxy_set_header X-Service-Name $service_name;
proxy_set_header X-Service-Port $service_port;
{{ end }}
{{ if not (empty $location.Backend) }}