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

@jamesh Great, then we could expect a fix in the base Snaps soon?

For the workaround for the time being in the application Snaps I have a question. You write:

I have taken the nsswitch.conf from the package as I assumed that it is the stage packages are the from the same repository/Ubuntu version as from the base Snap used (core20 in my case).

Is the build of the Snap happening under the same base Snap as the execution of the Snap will take place? And how can I grab /etc/nsswitch.conf from the base Snap under which I am building to include an edited version of it in my Snap?

The scripts used to build the base snaps from the Ubuntu archive modify the file:

Snapcraft should install your base snap in the build environment, so you can probably access this as /snap/core20/current/etc/nsswitch.conf.

As for actually implementing mdns hostname lookup by default, I’d like to check whether it’s something that is likely to be accepted before doing the work.

we discussed it in the core16 times and back then it would not have been denied, though we didn’t do any tests on UC devices wether it potentially delays DNS lookups …

but indeed things changed in UC20 and the core20 snap has removed a lot of stuff for size reasons that you still had around in 16 and 18. might be that you need additional dependencies added for libnss-mdns.

@jamesh, thank you very much. I have updated my Snaps now, here are the commits: CUPS Snap, PostScript Printer Application

For anyone who wants to add my workaround to his/her Snap, here are the needed changes for 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:
  [...]
  utils:
    plugin: nil
    stage-packages:
      - perl-base
    override-prime: ""

  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
      perl -p -e '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
    after: [utils]

  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
    plugin: nil
    stage-packages:
      - libnss-mdns
    prime:
      - lib/x86_64-linux-gnu/libnss_mdns*

  [...]

So let us hope that we do not have to keep this workaround for a long time.

@ogra, seems that there is no problematic dependency in libnss-mdns:

$ apt info libnss-mdns
[...]
Depends: avahi-daemon (>= 0.6.16-1), base-files (>= 3.1.10), libc6 (>= 2.14)
[...]
$ ldd  /lib/x86_64-linux-gnu/libnss_mdns.so.2 
	linux-vdso.so.1 (0x00007ffeb0591000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f5c93a99000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c938ad000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f5c93ad5000)
$

The dependency on avahi-daemon is there because it gets the resolution from avahi-daemon, the libraru errors quickly and gracefully if avahi-daemon is not there as @jamesh told. So somehow in the build process of the base Snap one needs to override the dependency on avahi-daemon or remove/not prime avahi-daemon afterwards (in case the external, classically installed avahi-daemon of the system is supposed to be used).

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