lua-resty-auto-ssl icon indicating copy to clipboard operation
lua-resty-auto-ssl copied to clipboard

Checking DNS records before issuing a certificate

Open discobean opened this issue 9 years ago • 5 comments

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.

discobean avatar Sep 27 '16 00:09 discobean

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.

GUI avatar Oct 22 '16 20:10 GUI

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 avatar Jan 07 '17 08:01 waynerobinson

@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.

GUI avatar Jan 13 '17 05:01 GUI

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.

andrewhamon avatar Oct 19 '17 09:10 andrewhamon

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.

mauriciogior avatar Sep 01 '20 05:09 mauriciogior