ingress-nginx-helm/rootfs/etc/nginx/lua/util/dns.lua
Thomas Jackson b698699fdd More helpful DNS failure message
Previously if dns.lua failed to resolve a name you'd see the following in your logs:
```
2019/10/12 23:39:34 [error] 41#41: *6474 [lua] dns.lua:121: dns_lookup(): failed to query the DNS server:
server returned error code: 3: name error
server returned error code: 3: name error, context: ngx.timer
```

Unfortunately this doesn't tell you what name is failing (so you have to start guessing). To alleviate the pain this simply adds the host name we are attempting to resolve to the log line so users don't have to guess.
2019-10-14 08:14:35 -07:00

160 lines
4.5 KiB
Lua

local resolver = require("resty.dns.resolver")
local lrucache = require("resty.lrucache")
local resolv_conf = require("util.resolv_conf")
local ngx_log = ngx.log
local ngx_INFO = ngx.INFO
local ngx_ERR = ngx.ERR
local string_format = string.format
local table_concat = table.concat
local table_insert = table.insert
local ipairs = ipairs
local tostring = tostring
local _M = {}
local CACHE_SIZE = 10000
local MAXIMUM_TTL_VALUE = 2147483647 -- maximum value according to https://tools.ietf.org/html/rfc2181
-- for every host we will try two queries for the following types with the order set here
local QTYPES_TO_CHECK = { resolver.TYPE_A, resolver.TYPE_AAAA }
local cache
do
local err
cache, err = lrucache.new(CACHE_SIZE)
if not cache then
return error("failed to create the cache: " .. (err or "unknown"))
end
end
local function cache_set(host, addresses, ttl)
cache:set(host, addresses, ttl)
ngx_log(ngx_INFO, string_format("cache set for '%s' with value of [%s] and ttl of %s.",
host, table_concat(addresses, ", "), ttl))
end
local function is_fully_qualified(host)
return host:sub(-1) == "."
end
local function a_records_and_min_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 ans.ttl < ttl then
ttl = ans.ttl
end
end
end
return addresses, ttl
end
local function resolve_host_for_qtype(r, host, qtype)
ngx_log(ngx_INFO, string_format("resolving %s with qtype %s", host, qtype))
local answers, err = r:query(host, { qtype = qtype }, {})
if not answers then
return nil, -1, err
end
if answers.errcode then
return nil, -1, string_format("server returned error code: %s: %s", answers.errcode, answers.errstr)
end
local addresses, ttl = a_records_and_min_ttl(answers)
if #addresses == 0 then
local msg = "no A record resolved"
if qtype == resolver.TYPE_AAAA then msg = "no AAAA record resolved" end
return nil, -1, msg
end
return addresses, ttl, nil
end
local function resolve_host(r, host)
local dns_errors = {}
for _, qtype in ipairs(QTYPES_TO_CHECK) do
local addresses, ttl, err = resolve_host_for_qtype(r, host, qtype)
if addresses and #addresses > 0 then
return addresses, ttl, nil
end
table_insert(dns_errors, tostring(err))
end
return nil, nil, dns_errors
end
function _M.lookup(host)
local cached_addresses = cache:get(host)
if cached_addresses then
return cached_addresses
end
local r, err = resolver:new{
nameservers = resolv_conf.nameservers,
retrans = 5,
timeout = 2000, -- 2 sec
}
if not r then
ngx_log(ngx_ERR, string_format("failed to instantiate the resolver: %s", err))
return { host }
end
local addresses, ttl, dns_errors
-- when the queried domain is fully qualified
-- then we don't go through resolv_conf.search
-- NOTE(elvinefendi): currently FQDN as externalName will be supported starting
-- with K8s 1.15: https://github.com/kubernetes/kubernetes/pull/78385
if is_fully_qualified(host) then
addresses, ttl, dns_errors = resolve_host(r, host)
if addresses then
cache_set(host, addresses, ttl)
return addresses
end
ngx_log(ngx_ERR, "failed to query the DNS server for %s:\n%s", host, table_concat(dns_errors, "\n"))
return { host }
end
-- for non fully qualified domains if number of dots in
-- the queried host is less than resolv_conf.ndots then we try
-- with all the entries in resolv_conf.search before trying the original host
--
-- if number of dots is not less than resolv_conf.ndots then we start with
-- the original host and then try entries in resolv_conf.search
local _, host_ndots = host:gsub("%.", "")
local search_start, search_end = 0, #resolv_conf.search
if host_ndots < resolv_conf.ndots then
search_start = 1
search_end = #resolv_conf.search + 1
end
for i = search_start, search_end, 1 do
local new_host = resolv_conf.search[i] and string_format("%s.%s", host, resolv_conf.search[i]) or host
addresses, ttl, dns_errors = resolve_host(r, new_host)
if addresses then
cache_set(host, addresses, ttl)
return addresses
end
end
if #dns_errors > 0 then
ngx_log(ngx_ERR, "failed to query the DNS server for %s:\n%s", host, table_concat(dns_errors, "\n"))
end
return { host }
end
if _TEST then
_M._cache = cache
end
return _M