Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dnsdist rules for DoT and DoH connections to detect the DNS server name #7210

Closed
M4t7e opened this issue Nov 19, 2018 · 4 comments

Comments

Projects
None yet
3 participants
@M4t7e
Copy link

commented Nov 19, 2018

  • Program: dnsdist
  • Issue type: Feature request

Short description

For DoT and DoH it would be very useful to differenciate between different services based on the DNS servers domain name. With TLS based connections a DNS client can add the Server Name Indication (SNI) in the Client Hello message. The idea is to store the SNI for the whole session context and make it available via a dnsdist rule. For DoH the server name can be extracted from the request URI.

Other information

For DoH it can be extracted from the request URI:
https://service1.example.com/dns-query

For DoT and DoH it can be extracted from the SNI in the Client Hello:
Extension: server_name (len=25)
Type: server_name (0)
Length: 25
Server Name Indication extension
Server Name list length: 23
Server Name Type: host_name (0)
Server Name length: 20
Server Name: service1.example.com

Usecase

Example rules in dnsdist:

  • TLSServerNameRule(servername)
    Matches queries with SNI value specified in servername.

  • HTTPServerNameRule(servername)
    Matches queries with DNS server domain name specified in servername.

If there would be a possibility to detect the DNS server name with dnsdist rules, there are countless possibilities. Here two examples:

Enable or disable options based on the DNS server name:

addAction(TLSServerNameRule("no-ecs.example.com"), DisableECSAction())
addAction(HTTPServerNameRule("no-ecs.example.com"), DisableECSAction())
addAction(AllRule(), PoolAction("DEFAULT"))

Send query to special downstream servers that offer different services (e.g. adblock, family-shield, safesearch for popular search engines, DNSSEC, etc.)

addAction(TLSServerNameRule("adblock.example.com"), PoolAction("BLOCK-ADS"))
addAction(HTTPServerNameRule("adblock.example.com"), PoolAction("BLOCK-ADS"))
addAction(AllRule(), PoolAction("DEFAULT"))

Description

It seems that DoT and DoH are becoming more and more popular and the number of (native) client implemenations is increasing. For those two DNS transports it is normally possible to set a domain name or a URL (just DoH) in the DNS resolver setting. Also https://dnscrypt.info/stamps-specifications is saying the hostname will "be used as a SNI name" for DoT and DoH.

Here some interesting DoT/DoH implementations:

In the past the only possibility to differenciate between different services was to have multiple different IPs on the same DNS Resolver or different ports (might be very hard to configure for most DNS clients). Another options is to make use of the source IP address. But this is sometimes very hard because you need to know the current IP address in realtime and especially for mobile devices the IP changes all the time because of different networks (RAN, WiFi, etc.). The DNS server domain name will not change and is independent of IP addresses, NAT scenarios and transport networks. Making use it to offer different services for DoT and DoH connections can be very useful for a lot of applications.

@M4t7e

This comment has been minimized.

Copy link
Author

commented Nov 21, 2018

I have just tested DoT ("Private DNS") with an Android 9 Pie emulator. It is always sending the SNI in Client Hello messages. And btw, it looks like it creates three connections to the DoT server in parallel.

@Habbie

This comment has been minimized.

Copy link
Member

commented May 7, 2019

An experiment, just for testing SNI extraction with OpenSSL (Debian Buster, version 1.1.1b-2) and (Debian Buster, libgnutls30 3.6.6-2): https://gist.github.com/Habbie/31a5a6a6dc15d1ba727fe3f1d23a968c

Observations:

  • both APIs support having more than one SNI; however, the TLS spec, as of RFC6066 and RFC8446, says there can only be one SNI, and there is only one defined type, that of a DNS hostname
  • those DNS hostnames are encoded in ASCII, with IDN names encoded as A-labels, i.e. xn--.... Names have no trailing dot, so for example, a legal SNI would be www.powerdns.com.) Nothing in the libraries promises that what we will receive is a valid DNS name. My proposal is to try to turn the SNI into a DNSName, and if that throws an exception, pretend there was no SNI.
  • RFC8446 (TLS1.3) (page 57) mentions that changing the SNI on session resumption is allowed, but discouraged. I suspect that libssl will do the right thing for us on SSL_handshake, but I did not test.
  • I tested both implementations with openssl s_client in TLS1.2 and TLS1.3 mode

'Now we just have to type it in'.

@Habbie

This comment has been minimized.

Copy link
Member

commented May 9, 2019

Similar 'just get the SNI out' hack for DoH. https://gist.github.com/Habbie/0ab650579120ac01dd3578b9a1a4215f

Note that this one wouldn't even know where to store the name.

After discussion with @pieterlexis and @rgacogne we realise that addXLocal is not the best interface to begin with, and that the list of paths in addDOHLocal really overstretches the concept. Ideally we'd rearchitect some of this. When we do that, we can also support separate certs for separate SNIs.

@M4t7e

This comment has been minimized.

Copy link
Author

commented May 10, 2019

After discussion with @pieterlexis and @rgacogne we realise that addXLocal is not the best interface to begin with, and that the list of paths in addDOHLocal really overstretches the concept.

This would also be difficult especially with wildcard certificates or similar... My idea was to use a multidomain or a wildcard TLS certificate (e.g. *.example.com) and the different SNI names can be dynamically used with the dnsdist rule functions:

  • addAction(TLSServerNameRule("no-ecs.example.com"), DisableECSAction())
  • addAction(TLSServerNameRule("feature-a.example.com"), PoolAction("Feature-A"))
  • addAction(TLSServerNameRule("feature-b.example.com"), PoolAction("Feature-B"))
  • etc.

And if possible, make the SNI available in the DNSQuestion object (e.g. dq.sniName) for dynamic and custom Lua actions, debugging, etc.

@rgacogne rgacogne referenced this issue May 16, 2019

Merged

dnsdist: Implement SNIRule for DoT and DoH #7825

6 of 7 tasks complete

@rgacogne rgacogne added this to the dnsdist-1.4.0 milestone May 16, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.