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

client (unix, lwt): in "create" parse /etc/resolv.conf and use first IPv4 address of a nameserver #241

Merged
merged 1 commit into from Nov 4, 2020

Conversation

hannesm
Copy link
Member

@hannesm hannesm commented Oct 15, 2020

this changes the default behaviour by using the system-configured nameserver(s).

there are some issues with this strategy:

  • the system-configured nameserver may be UDP only, in which case the client will fail
  • the first nameserver may be unreachable

…IPv4 address of a nameserver

this changes the default behaviour by using the system-configured nameserver(s).

there are some issues with this strategy:
- the system-configured nameserver may be UDP only, in which case the client will fail
- the first nameserver may be unreachable
@hannesm
Copy link
Member Author

hannesm commented Oct 15, 2020

to fix "all the issues" [tm], we need to adjust the code further (to also accomodate upcoming IPv6 support)

  • the nameserver in the context should be a list (eventually mutable, or a mutable index pointer i to the last successfully used one, and create should take a ~nameservers list)
  • the nameservers from /etc/resolv.conf being added twice: once with udp, once with tcp
  • UDP client (including retransmissions) should be implemented
  • eventually (as in dns-stub) a persistent TCP connection should be used in the DNS client as well (to avoid lots of connections)

once we have these, a DNS lookup should be roughly of the form:

  • attempt to connect to nameserver[i] for resolution
  • if this fails, use nameserver[i + 1] (until all nameservers are visited, at which point resolution failed)

does this sound reasonable @cfcs? the reasoning is that on create we don't want to do "test lookups", and also we should accomodate potentially changing network connections over the lifetime of an OCaml program using the dns-client library (hmm, do we need to poll /etc/resolv.conf? is there a common methodology (file system watchers, eventually what irmin-watch?) to get notified upon changes thereof?)

@cfcs
Copy link
Contributor

cfcs commented Oct 27, 2020

@hannesm I think that sounds reasonable, yes. :)

Some inputs:

  1. on linux (I don't know how it looks on BSD) there are various options you can set inline in resolv.conf, for instance option rotate makes it pick a random server from the list instead of the left fold. I'm not sure if it's good idea to stick with that notion, since in practice people usually want that to be the default, but few people use the option syntax.
  • another option is option timeout:123 (to specify a 123 second timeout). As mentioned above I think we should go ahead and define rotation strategies and timeouts ourselves, but if the user DID express these, maybe it's worth exposing this information from the resolv.conf parser.
  1. Again on Linux you would usually use a inotify handle to listen for changes instead of polling. On modern Linux systems with systemd, /etc/resolv.conf will usually be a symlink to a systemd-managed file (that gets rewritten every so often, not sure when exactly). If we want to make it work there we likely need to monitor both the symlink and each (one?) layer of indirection until we get to the actual file. This seems like a lot of complexity; just reading the file every n seconds (n = 30 ?) seems much easier to me, and unlikely to cause too much annoyance.

Copy link
Contributor

@cfcs cfcs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another input: What do we do about domains, e.g. if the resolv.conf contains search example.com and we want to lookup test that would turn into a request for test.example.com (perhaps also a lookup for test when it's not a fqdn, not sure)?

@hannesm
Copy link
Member Author

hannesm commented Oct 27, 2020

@cfcs thanks for your input. The resolvconf parser can be enhanced (I found the rust parser https://github.com/tailhook/resolv-conf/tree/master/src a very good resource of what options are available on different operating systems).

Now, the question arises "what should the behaviour be?" if you're keen on the exact same semantics as (g)libc, I guess we're better off just using getaddrinfo (in the light of "all other applications on the system are using that function", the additional attack surface by OCaml applications using it is rather small) -- for reducing the attack vector (decoding malicious DNS packets), maybe it makes more sense to advertise an OCaml stub resolver which talks to the Internet, and is used by (g)libc in /etc/resolv.conf -- the OCaml resolver would decode and encode the packets, and thus act as a sanitizer?

Considering that OCaml's DNS client will diverge from getaddrinfo, I'm open for suggestions of the intended semantics:

  • support domain / search -- do you use these, do you know of people using it? to me this is a bit questionable (and originates in the old times when there were large internal networks and short hostnames, these days I see split-horizon setups or internal domains which are fully qualified by the user)
  • option rotate - I was not aware this exists, and am still not sure what the semantics should be
  • option timeout - that's sensible
  • nameserver YYY - that's the information we currently parse, it is slightly unclear how protocol selection should happen

Now, as you mention, /etc/resolv.conf may be modified (when changing network, ...) - so "remember the has of its contents" and "re-read when resolution fails" sounds like a good option. In a given network some of the nameservers may be unreachable (temporary or permanent), some only available via UDP (TCP/TLS). That's all not well specified in /etc/resolv.conf. So I'd suggest to remember a list of pairs (protocol, IP) of nameservers and a mutable pointer to the "last one that worked" (likely as well a timestamp for "this entry was successfully used at point Y").

An interesting project is unwind https://man.openbsd.org/unwind from where we should take some ideas - it is a validating resolver, where Florian put in quite some work in testing in different scenarios.

To iteratively move forward, my suggestion is (where the checkboxes are intended for the next release, (later) for some future release):

  • together with mirage: dual stack (Mirage_stack.V4V6) support #239 we're breaking the API anyways, let's have create receive a nameserver list
  • work on enhancing the resolv-conf parser (timeout, ..) and the DNS client semantics (remember which NS was tried last, ...)
  • get UDP support in the client(s) -- we can retain TCP as default (eventually with caching the TCP connection, as done in dns-stub), but mainly need retransmission (on lookup timeout)
  • domain / search support for resolv.conf
  • (later) add DNSSec validation (needed for a reasonable recursive resolver anyways - need some magic to have it disabled when in a network where you've to browse somewhere and click ok)
  • (later) add DNS-over-TLS support (here question arise in the form of "where does the certificate come from" etc.)

@cfcs
Copy link
Contributor

cfcs commented Oct 28, 2020

@hannesm Sounds good to me with a nameserver list in create and the smart(er) timeouts etc.

  • Yeah, I use search for both resolv.conf and for SSH, but I don't think they're strictly necessary. If I had to use a system that did not support this, I would just copy-paste the full domains. I think most people can live with that.

  • I think the option syntax is a bit limited; in practice if I want to configure things I'd like to be able to configure each server individually. I would not worry about that too much and just try to come up with something sensible, perhaps taking inspiration from the unwind thing where applicable. For instance I think we should default to TCP and not require the user to put option use-vc (whatever that means) in resolv.conf in order for that to happen.

@hannesm
Copy link
Member Author

hannesm commented Nov 4, 2020

I copied the list above into a separate issue, together with "remember hash of resolv.conf and re-parse when resolution fails (and hash was modified)" -- this would be convenient for laptops roaming between networks. I'll merge this PR as is.

@hannesm hannesm merged commit 3dae703 into mirage:master Nov 4, 2020
@hannesm hannesm deleted the client-resolv-conf branch November 4, 2020 20:31
kit-ty-kate pushed a commit to ocaml/opam-repository that referenced this pull request Jan 11, 2021
…ns-client, dns-server, dns-tsig and dns-cli (4.6.3)

CHANGES:

* dns-server: wildcard support (mirage/ocaml-dns#248 @hannesm)
* dns-certify: only dnskey needs to be a valid hostname (mirage/ocaml-dns#247 @hannesm),
  allow [`raw] Domain_name.t in signing requests (mirage/ocaml-dns#249 @hannesm)
* dns-client.resolvconf provides a parser for /etc/resolv.conf (mirage/ocaml-dns#240 @hannesm),
  used in dns-client.unix and dns-client.lwt (mirage/ocaml-dns#241 @hannesm)
* BUGFIX dns-cli notify keys are accepted in namekey_c (mirage/ocaml-dns#242 @hannesm)
* BUGFIX dns: revise TXT resource record encoding and storage (for DKIM usage)
  previously RR were cut at 255 characters (fixes mirage/ocaml-dns#244, mirage/ocaml-dns#245 @hannesm)
* BUGFIX dns: decoding of TSIG packets (mirage/ocaml-dns#250 @hannesm)
* BUGFIX ocertify: pem file may contain a certificate chain (mirage/ocaml-dns#246 @hannesm)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants