Checking DNS records before issuing a certificate
First thanks for the great work in building this 👍
It would be great if auto-ssl would check that the DNS would resolve to a certain set of IPs before issuing a request to LetsEncrypt.
For example a new auto_ssl:set function could return that list of IPs, and if DNS resolves to any of those the certificate request goes ahead.
It should be possible to implement something like this with a custom allow_domain method and integrating it with lua-resty-dns (lua-resty-dns-cache may also be useful in this case). I can definitely see the use-cases behind this, but I'm not sure integrating this functionality directly into lua-resty-auto-ssl would be the best fit. But if anyone comes up with an example allow_domain using one of the resty-dns libraries, we could definitely provide that as an example.
I'm not sure if this is the best code (I'm completely unfamiliar with Lua), but I have implemented this using lua-resty-dns-cache largely from the example on the two library's sites:
In the server section of Ngxin:
lua_shared_dict dns_cache 1m;
In the init_by_lua_block
require("resty.dns.cache").init_cache(200)
And then the allow_domain function:
auto_ssl:set("allow_domain", function(domain)
if domain == "whitelisted-domain.com" then
return true
end
local DNS_Cache = require("resty.dns.cache")
local dns = DNS_Cache.new({
dict = "dns_cache",
negative_ttl = 5,
max_stale = 300,
resolver = {
nameservers = {"8.8.8.8"}
}
})
local answers, err, stale = dns:query(domain)
if err then
if stale then
ngx.header["Warning"] = "110: Response is stale"
answer = stale
ngx.log(ngx.ERR, err)
else
ngx.status = 500
ngx.say(err)
return ngx.exit(ngx.status)
end
end
if not answers then
ngx.say("failed to query the DNS server: ", err)
return false
end
if answers.errcode then
ngx.say("server returned error code: ", answers.errcode,
": ", answers.errstr)
return false
end
for i, ans in ipairs(answers) do
-- If this CNAME exists at all in the result, generate an SSL certificate.
if ans.cname == "destination.example.com" then
return true
end
end
return false
end)
@waynerobinson: Thanks for contributing this example! I'll try to give this a spin at some point and add in the README. Or if anyone else gets a chance to test this, it would be great to know if it works for you.
Depending on your use case, using DNS may not produce the expected results. If a domain is using, e.g. Cloudflare CDN, the DNS records will point to a different IP than your server.
Probably not a huge deal, especially since Cloudflare can be configured to accept untrusted certs from the origin server, but worth considering.
Up. I think this has great value since there could be an exploit and end up flooding Let's Encrypt API with wrong or invalid DNS information (eg. set up a local DNS for random-non-existent-domain.xyz that points to your IP and it would try to generate a certificate for an invalid domain).
The workaround we found is to setup a redis and verify if the domain is whitelisted on allow_domain.