No mdns support in snaps (should core have a modified nsswitch.conf ?)

I think mdns4_minimal is unlikely to trigger problems. From it’s readme file:

libnss_mdns{4,6,}_minimal.so (new in version 0.8) is mostly identical to the versions without _minimal. However, they differ in one way. The minimal versions will always deny to resolve host names that don’t end in .local or addresses that aren’t in the range 169.254.x.x (the range used by IPV4LL/APIPA/RFC3927.) Combining the _minimal and the normal NSS modules allows us to make mDNS authoritative for Zeroconf host names and addresses (and thus creating no extra burden on DNS servers with always failing requests) and use it as fallback for everything else.

So the plugin is going to be inert for most hostname lookups that don’t end in .local.

The plugin only links to libresolv.so.2 and libc.so.6, so the only additional library is the NSS plugin itself. The plugin doesn’t require any extra libraries because it doesn’t use Avahi’s full D-Bus API. Instead it speaks a simple line oriented protocol over the /run/avahi-daemon/socket socket. For example:

$ echo 'RESOLVE-HOSTNAME-IPV4 scruffy.local' | nc -U /run/avahi-daemon/socket
+ 2 0 scruffy.local 192.168.0.121

You will of course need Avahi installed for this lookup to succeed (either a traditional package on classic distros, or the snap on Ubuntu Core). But if Avahi is missing, it should return as soon as it fails to connect to the unix socket.

that sounds indeed all fine then …

You can probably omit this and replace with a build-packages entry for perl-base onto the nsswitchconf part. Alternatively use sed for your substitution, which is most likely available in all build environments out of the box:

sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf

I also prefer making file modifications in override-pull, but that’s my own preference.

1 Like

@lucyllewy, thank you very much for the hints. First I tried with sed but switched to perl as I did not know about the -E which allows richer regular expressions.

Second, I would like to know that if I define a build package whether it is already available during pull and whether it is still available for stage and prime, or only for the build itself.

Am I also right that the build packages do not get primed (and so not shipped with the Snap)?

I’ve put together a draft PR adding mDNS resolution support to the core20 snap here:

I used equivs-build to create a fake avahi-daemon deb as a way to avoid installing it plus its dependencies into the image, but I’m open to suggestions if there is a better way to handle it. Local testing shows that a snap plugging “network” can lookup mDNS names via getent hosts xyz.local.

If Avahi is not present, then the plugin returns a status of UNAVAIL, which causes resolution to continue on to the next plugin. So there should be no change to name resolution on an Ubuntu Core 20 device without Avahi installed.

1 Like

build-packages are available from a part’s pull step onwards.

Snapcraft installs any build-packages before running the part that defines the requirement’s pull step - these packages are installed onto your build system with apt-get and marked as automatically installed (so an apt-get autoremove will clean them) and so are available for use for the remainder of the build. The build-packages are not staged into your snap at all, unless you also add them to stage-packages.

1 Like

The build process for the base snaps is a bit different. Most of the build occurs within a chroot to configure the set of files that will make up the snap. I can’t see anything installed on the build system when running apt-get install libnss-mdns inside that chroot.

1 Like

@lucyllewy, I have now updated my Snaps (commits: CUPS Snap, PostScript Printer Application) following your hint to simplify the implementation of the mDNS hostname look-up support. Thanks you very much.

For anyone who also wants to add mDNS hostname look-up support, here is what is needed in snapcraft.yaml:

[...]
# Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
# working: Mirror nsswitch.conf with added mDNS look-up support into
# /etc/nsswitch.conf
layout:
  /etc/nsswitch.conf:
    bind-file: $SNAP/etc/nsswitch.conf
[...]
parts:
  [...]
  nsswitchconf:
    # Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
    # working: Take the original nsswitch.conf file from the base
    # Snap and add "mdns4_minimal [NOTFOUND=return]" to its "hosts:" line
    # See: https://forum.snapcraft.io/t/no-mdns-support-in-snaps-should-core-have-a-modified-nsswitch-conf/
    plugin: nil
    override-prime: |
      set -eux
      sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
      snapcraftctl prime
    prime:
      - etc/nsswitch.conf

  libnss-mdns:
    # Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
    # working: Install the libnss-mdns libraries needed for mDNS host name
    # look-up
    # See: https://forum.snapcraft.io/t/no-mdns-support-in-snaps-should-core-have-a-modified-nsswitch-conf/
    plugin: nil
    stage-packages:
      - libnss-mdns
    prime:
      - lib/x86_64-linux-gnu/libnss_mdns4_minimal*
  [...]

Is there any specific reason to not put both into one part to not bloat your snapcraft.yaml ? i.e. like:

parts:
  [...]
  nsswitchconf:
    plugin: nil
    override-prime: |
      set -eux
      sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
      snapcraftctl prime
    stage-packages:
      - libnss-mdns
    prime:
      - lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal*
      - etc/nsswitch.conf

I’d caution against people cargo culting this part around. Ideally we’ll have a solution available in the base snap in reasonable time, making it unnecessary.

It makes sense for Till to use this in his CUPS snap to remain unblocked (since mDNS hostname resolution is core to how parts of the print stack works), but it’s probably not worth spreading it beyond that.

@ogra, thanks, and especially thanks as you have also corrected a bug with it, using $SNAPCRAFT_ARCH_TRIPLET as my version with x86_64-linux-gnu was naturally not working on non-amd64 systems.

I have updated my Snaps again now. The complete changes in snapcraft.yaml needed to add mDNS look-up of .local host names are now:

[...]
# Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
# working: Mirror nsswitch.conf with added mDNS look-up support into
# /etc/nsswitch.conf
layout:
  /etc/nsswitch.conf:
    bind-file: $SNAP/etc/nsswitch.conf
[...]
parts:
  [...]
  mdns-lookup:
    # Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
    # working: Take the original nsswitch.conf file from the base
    # Snap and add "mdns4_minimal [NOTFOUND=return]" to its "hosts:" line
    # Also install corresponding mdns4_minimal plug-in
    # See: https://forum.snapcraft.io/t/no-mdns-support-in-snaps-should-core-have-a-modified-nsswitch-conf/
    plugin: nil
    stage-packages:
      - libnss-mdns
    override-prime: |
      set -eux
      sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
      snapcraftctl prime
    prime:
      - lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal*
      - etc/nsswitch.conf
  [...]
1 Like

@jamesh, the solution being in the base Snap is really the best solution, to avoid everyone who needs the mDNS lookup to add the workaround which we are discussing here. Any Printer Application will need it, CUPS needs it, and any application for managing and using network devices (IoT, smart home, routers, access points, NAS, …) will need it.

If there are some reasons to not have the support in the Core Snap (security, privacy, …) and only the application Snaps which actually need it should have it enabled, then we probably should have some interface, extension, … which the Snap developer can add to his snapcraft.yaml to activate mDNS lookup, instead of having to add the lines which I am showing in my previous post. This should (by default) give the minimum support of only resolving .local names.

Any solution performed on the host system (change systemd-resolved config, install nscd, …) is not viable, as Snaps should be distribution-independent, and a change which we introduce to distributions now will take years to get adopted everywhere or does not get adopted by some at all.

For any solution in the application Snaps we naturally need to warn the developers about the risks and tell them only to use it if needed.

If we allow the support only by-application (by the application Snaps to activate it) then I recommend that besides printing and other device/IoT/…-related Snaps also web browsers allow the .local lookup as many device’s manufacturers would tell you to access the device’s web admin interface via the device’s XXX.local host name. As this use case is common practice it should work.

I think it is likely now that the core20 PR will be accepted, so it is premature to start patching lots of snaps. I agree that mDNS hostname resolution is useful for web browsers, but we’ve lived with this broken up to now so waiting a few extra weeks shouldn’t be a big deal.

Do you need to patch more than just the CUPS snap? As I understand it, printer application snaps are just localhost HTTP servers that advertise themselves via mDNS service discovery. Their clients (which I think is just cupsd?) may need hostname lookup if they try to connect to the host name in the service discovery record (rather than the address), but it’s not clear the printer application itself needs it.

@jamesh OK, the Chromium is not urgent, but it should soon get working (PR getting accepted should be good enough) as users should be able to access web admin interfaces of network devices.

On the CUPS Snap mDNS lookup is needed to connect to printers and to remote CUPS servers in the local network. If you have a modern, driverless network printer CUPS works out-of-the-box with it if CUPS does mDNS lookup. Also sharing printers is easy with it. You simply share a CUPS printer on one machine in your local network and all the other machines can print on this queue right away, if their CUPS can mDNS lookup the name of the machine which is sharing. This is the so-called ZeroConf networking with each computer or device providing its host name via mDNS, no need to register names on a DNS server, especially important for home and small office networks where there is no system administrator.

For Printer Applications it depends on what kind of physical printers they access. Printer Applications are typically a replacement for classic printer drivers, emulating an IPP printer on their front and on their back passing on the incoming jobs to a physical printer, which does not IPP nor uses standard printer languages. The Printer Application needs mDNS lookup only if it supports network printers which use the mDNS/ZeroConf mechanism to make their host names available. A Printer Application which supports only USB printers or very old network printers without mDNS/ZeroConf support do not need to do mDNS lookup.

The server end of of a Printer Application (IPP printer on localhost) does not need mDNS host name lookup.

If you want to test out the modified core20, you can download it here:

Assuming you’re logged in to Github, you should be able to download the snaps.zip file from the “Artifacts” section of that page. After unpacking it, you can install the snap with:

sudo snap install --dangerous core20_20210423_amd64.snap

This should replace the version of the snap from the store. After testing, you can switch back to the version from the store with:

sudo snap refresh --amend --stable core20

@jamesh, thank you very much! I have tested your core20 test package now and it works!

It took me some time until getting everything neutralized and reproducing the original problem. First, with your test core20 installed one cannot build Snaps. I had to go back to the originals to build a modified CUPS (workaround removed) for reproducing the problem. I also had to snap clean the build and to purge the installed CUPS Snap to get really rid of the workaround. But finally I found back to the problem appearing, installed your corrected core20 and it works!

So from my side and the point of view that yiour changes solve the problem the new core20 is approved.

@jamesh, by the way, I have to re-install my (locally built) CUPS Snap after each change/installation of the core20 Snap. Will this go automatically if both core20 and CUPS Snaps get actually installed from the Snap Store?

@jamesh thank you very much! I have installed the new core20 from the edge channel now, rebuilt my CUPS Snap without the mDNS lookup workaround and it works. So the core20 does correct mDNS host name lookup now!

1 Like

As core20 fro the stable channels has the fix included, I have removed the workaround from all OpenPrinting Snaps now.

Note also that this workaround broke the Snaps on systems where /etc/nsswitch.conf is a symlink. See this bug report on Fedora with authselect.

Thank you very much again for this fix.

$ snap list
[...]
core20  20211115  1242  latest/stable  canonical✓  base
[...]
$ grep mdns /snap/core20/current/etc/nsswitch.conf 
hosts:          files mdns4_minimal [NOTFOUND=return] dns
$

Sorry for the bump, but since this discussion seems to have prompted the fix, I think this is where my question belongs.

Why is obsolescence being built into this fix? If you use mdns_minimal instead of mdsn4_minimal you will also get IPv6 mDNS support…