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

WIP: resolve: add search domain and resolveconf path settings #744

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

corngood
Copy link

I'm just looking for feedback on this before I put any work into documentation/testing.

First, allowing the location of resolvconf to be specified. This is to help with non-FHS systems (NixOS in my case). An alternative here would be to depend on $PATH, but we'd then need to change how use_resolvconf is determined. Currently nixpkgs patches strongswan to depend directly on openresolv, but this doesn't account for other implementations of resolvconf, such as systemd-resolved.

Secondly, allow a search domain to be specified in the configuration fragment passed to resolvconf. Using this I can configure resolvconf to treat this as a private interface, which will in turn configure dnsmasq to use nameservers from charon only for the specified domain.

  • Is there a way I could configure search domains per-connection? I couldn't find an example of a similar setting.
  • It looks like https://datatracker.ietf.org/doc/html/rfc8598 would cover this, but I don't see any signs of implementation. Also, a client may still want to override it.

@tobiasbrunner
Copy link
Member

First, allowing the location of resolvconf to be specified.

I've no problem with that, but don't these other packages you mention provide a resolvconf symlink/script in PATH for compatibility? (Maybe only optionally?)

Secondly, allow a search domain to be specified in the configuration fragment passed to resolvconf. Using this I can configure resolvconf to treat this as a private interface, which will in turn configure dnsmasq to use nameservers from charon only for the specified domain.

That's not really what a search domain is, you are thinking about split-DNS (routing domain in systemd-resolved speak). Search domains are used as suffix for incomplete hostnames (e.g. makes vpn.example.org from vpn if example.org is a search domain). I'm not aware of an API that supports explicitly configuring split-DNS other than the D-Bus interface org.freedesktop.resolve1 provided by systemd-resolved, which we currently don't support and which is, unfortunately, interface bound of which there are none used/created by IPsec usually (and unlike resolvconf where we just pass a fake interface name, systemd-resolved insists on an existing interface). Or are you saying there is a mode/config option for dnsmasq where it treats search domains like routing domains and only uses the DNS server for the given search domains (it's technically not really the same, however, systemd-resolved seems to do the same if a complete name matching the search domain is passed)? Also note that RFC 8598 exchanges routing domains and not search domains (for which there are currently no config payloads defined in IKEv2), but I guess it's up to clients to treat them as search domains too.

@corngood
Copy link
Author

I've no problem with that, but don't these other packages you mention provide a resolvconf symlink/script in PATH for compatibility? (Maybe only optionally?)

They typically do, but the resolve plugin currently uses stat to locate it at a fixed path and falls back to updating resolv.conf if it doesn't exist. We'd need to add a flag to force it to exec resolvconf regardless of the existence of the file, or use a different method to determine if it's executable. If you have a preference there I'd be happy to investigate it.

Or are you saying there is a mode/config option for dnsmasq where it treats search domains like routing domains and only uses the DNS server for the given search domains (it's technically not really the same, however, systemd-resolved seems to do the same if a complete name matching the search domain is passed)?

Yeah, so if you tell resolvconf that an interface is private, it will treat the search domain from the resolv.conf fragment as a routing domain when it configures certain 'subscribers'. With dnsmasq this results in a config like server=/domain/address. In this case the domain is not actually used as search domain, and is only used for the specified domain.

I'm happy to rename things, but technically what's being passed to resolveconf is a search domain:

search domain
nameserver address

This all adds up to a working split-DNS, but it's quite messy.

I'd be happy to move to systemd-resolved, but ran into the issue you mentioned, which I think is covered by systemd/systemd#5573.

@tobiasbrunner
Copy link
Member

They typically do, but the resolve plugin currently uses stat to locate it at a fixed path and falls back to updating resolv.conf if it doesn't exist.

I guess that could be fixed with another symlink at that path (stat() should work fine on that). Or we could try multiple paths if there are other well known ones. What's the path in your case?

We'd need to add a flag to force it to exec resolvconf regardless of the existence of the file, or use a different method to determine if it's executable.

The former wouldn't work if we'd still call the absolute path so we'd have to switch to calling it just as resolvconf if such a flag was set (doesn't seem better than making the path configurable, though). Testing if it's executable would easiest be done by just executing it. Since our process wrappers don't provide a way to determine if executing a command failed because the executable didn't exist or it returned an error status, we'd have to call it in way that doesn't cause it to error out. However, that seems to be surprisingly difficult without side-effects (i.e. installing a DNS server).

For instance, the Debian version only provides support for very limited arguments (e.g. no --help/-h or --version/-v) and exits with an error for unknown ones (or no arguments). So we'd basically be left with passing -a or -d. Installing a DNS server just to test the availability of resolvconf is obviously not ideal. While we could omit passing anything or only comments via STDIN, that might cause an error for implementations that explicitly check for proper input (it doesn't for the Debian version, but does for that provided by systemd). So we could instead try to delete a non-existent server/interface, but that might cause an error too (again, this isn't the case for the Debian version but the systemd version, which would ignore the error if -f was passed, however, that in turn causes the Debian version to fail). So I don't think actually calling resolvconf is an option.

Yeah, so if you tell resolvconf that an interface is private, it will treat the search domain from the resolv.conf fragment as a routing domain when it configures certain 'subscribers'.

Are you referring to the -p argument the openresolv version of resolvconf provides? (Which we currently don't pass, as neither the Debian, nor the systemd versions support it. As mentioned above, the former actually fails for unknown arguments.) Or is there another way (e.g. by naming the interfaces a specific way)? (I guess wrapping the actual resolvconf with a custom script could be an option.)

With dnsmasq this results in a config like server=/domain/address. In this case the domain is not actually used as search domain, and is only used for the specified domain.

Nice, does that support multiple domains? Because search apparently accepts multiple space/tab-separated domain names.

I'm happy to rename things, but technically what's being passed to resolveconf is a search domain:

No, that's OK (maybe make it search_domains, see above). But it could possibly be a non-resolvconf-specific option (i.e. remove .resolvconf and also use it when modifying resolv.conf). And not sure about it being a global option (although connection specific options are kinda tricky to implement for plugins). Ideally, we'd support the attributes defined by RFC 8598 (maybe with a global option to not request/process them). But that's a lot more work and it also affects several components outside the resolve plugin. So I guess as an initial solution such global setting would be fine.

I'd be happy to move to systemd-resolved, but ran into the issue you mentioned, which I think is covered by systemd/systemd#5573.

Ah, didn't know about that ticket. Would be great if there was an option to add DNS servers, domains etc. under a generic (i.e. non-interface) name.

@corngood
Copy link
Author

I guess that could be fixed with another symlink at that path (stat() should work fine on that). Or we could try multiple paths if there are other well known ones. What's the path in your case?

The path in my case (NixOS) is /run/current-system/sw/bin/resolvconf, which is a symlink to either openresolv or systemd depending on the system configuration. It's also in $PATH. I think it would be a bad idea to hard-code this sort of nix-ism into the plugin. At least /sbin/resolvconf makes sense in FHS.

However, that seems to be surprisingly difficult without side-effects (i.e. installing a DNS server).

Agreed. This seems very fragile.

I think my preference would be to either:

  • add a setting like use_resolvconf to override the stat check
  • infer use_resolvconf if either file or exec are set (only stat if neither are set, error if both are?)

Either of these would allow exec to be set to resolvconf to allow use of $PATH.

Are you referring to the -p argument the openresolv version of resolvconf provides?

I actually set a configuration option in resolvconf.conf:

    private_interfaces="lo.inet.ipsec.*"

I believe this does the same thing as -p. Maybe we could add a setting for extra arguments, to avoid having to configure resolvconf separately? It's not something we'd want to enable by default unless we implement RFC 8598.

Nice, does that support multiple domains? Because search apparently accepts multiple space/tab-separated domain names.

Yeah, it should. I'd like to support that in the plugin, I just need to add the conversion logic and test it.

No, that's OK (maybe make it search_domains, see above). But it could possibly be a non-resolvconf-specific option (i.e. remove .resolvconf and also use it when modifying resolv.conf).

Makes sense. I have yet to implement the resolv.conf handling.

And not sure about it being a global option (although connection specific options are kinda tricky to implement for plugins).

I'd like to find a way to configure this per-connection. The closest thing I could fine was eap-radius, where you specify profiles and then reference them from e.g. rightauth, as in pinprofile here: https://wiki.strongswan.org/projects/strongswan/wiki/EapRadius. What do you think?

@tobiasbrunner
Copy link
Member

  • infer use_resolvconf if either file or exec are set (only stat if neither are set, error if both are?)

Sounds good to me, not sure about the error, might be simpler to just ignore file if not used. I've pushed a change to that effect to the 744-resolvconf branch (includes a log message stating what's going to be used).

Maybe we could add a setting for extra arguments, to avoid having to configure resolvconf separately?

I wonder if resolvconf -p -d would work (it's only documented for -a), because if so, the configured command could include that argument. But doing that via resolvconf.conf seems not that bad either.

Yeah, it should. I'd like to support that in the plugin, I just need to add the conversion logic and test it.

The option could just be documented to expect space-separated domain names, like it's documented for resolv.conf. No need to convert anything.

I'd like to find a way to configure this per-connection. The closest thing I could fine was eap-radius, where you specify profiles and then reference them from e.g. rightauth, as in pinprofile here: https://wiki.strongswan.org/projects/strongswan/wiki/EapRadius. What do you think?

What the radius plugin does is very specific to authentication methods (and required a lot of changes all over the place to support it).

There are two relatively easy approaches that plugins may use if they want to make something connection-specific (based on the connection's name). Both use settings in strongswan.conf, not swanctl.conf and there are very few examples at the moment.

  1. One example is the p-cscf plugin that uses charon.plugins.p-cscf.enable.<conn> = yes|no to enable the functionality for a particular connection (so here it would be charon.plugins.resolve.search_domains.<conn> = "specific search domains", charon.plugins.resolve.search_domains could also be a value to define a default).
  2. The other approach is used by the forecast plugin, where charon.plugins.forecast.reinject takes a list of connection names for which the plugin is enabled (instead of using charon.plugins.forecast.reinject.<conn> = yes|no). Here it would require using anonymous sub-sections, something like charon.plugins.resolve.<arbitrary-name-or-maybe-with-common-prefix>.conns = <list of connection names> and charon.plugins.resolve.<arbitrary-name>.search_domains = "search domains" (again, there could be a default value directly under resolve). This had the advantage that multiple connections could easily share the same search domains, but it's more complicated to parse and connection names can't be added via include or section references.

Not sure if doing either is worth the effort right now. We could easily add this at some point if there is a need for it (maybe until then we have some kind of facility to add plugin-usable key-value pairs in swanctl.conf).

tobiasbrunner added a commit that referenced this pull request Dec 19, 2022
Prefer the configured command over finding it at the default location
over installing in the configured file.

References #744
@tobiasbrunner
Copy link
Member

Note that the change that adds the resolvconf.path option is now in master.

@cla-bot
Copy link

cla-bot bot commented May 9, 2023

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have the users @corngood on file. In order for us to review and merge your code, please contact the project maintainers via info@strongswan.org to get yourself added.

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