Snapping CUPS Printing Stack: Avahi support, system users/groups

it does not seem to be in path
I just run some test and if you check scheduler core, there are fchown calls, which are not guarded by run time or compile time conditions, so it will call them no matter what --with-system-groups is set
Just running more tests here and might have something for @till.kamppeter

yeah, those fchown calls are utterly useless, one is called in log file, another one on run time directory

Rememboring on what CUPS generally does is trying to keep the permissions and ownerships of all its files (at least in /etc/ and /var) correct. Perhaps it does fchwon calls for that but I am not sure.

@till.kamppeter I did a bit of refactoring to your snapcraft.yaml I moved to autotools plugin, to make building mode clean and ability to tweak it from snapcraft.yaml You can see there also attempt to remove fchown and chmod calls. There still seems to be some offending syscalls, so you need to keep iterating to remove them. You had even chown in your own helper script run-cupsd :slight_smile: Don’t use cp -na and don’t call chmod there, none is really needed. If you want particular file permissions, set them at build time, as you are anyway just coping those files from SNAP to SNAP_DATA From here you should be able to get it confined

@ondra, thank you for your PR. I have merged it, built the snap and installed it. Now the bad system call is, according to audit message in syslog, 92, which is chown (see here).

/var/log/syslog:

Nov 14 16:02:15 virt-devel kernel: [20888.239489] audit: type=1326 audit(1510682535.462:179): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=5713 comm="cupsd" exe="/snap/cups/x13/sbin/cupsd" sig=31 arch=c000003e syscall=92 compat=0 ip=0x7feedda722c7 code=0x0
Nov 14 16:02:15 virt-devel cups.cupsd[5660]: Bad system call (core dumped)

At a minimum, please use scmp_sys_resolver for resolving syscalls since the numbers will vary depending on architecture. Eg:

$ scmp_sys_resolver 92
chown
$ scmp_sys_resolver chown
92

Better still is to use ‘snappy-debug’ which will resolve the syscall for you and suggest things. See ‘Debugging’ under Security policy and sandboxing for details.

@jdstrand, what exactly does the snapcraft preload part do?

It is an LD_PRELOAD library that helps ease snapcrafting. It specifically has something for chown: https://github.com/sergiusens/snapcraft-preload/blob/master/preload.cpp#L422

If you didn’t want all of these, you could also strip it down to just what you want. Honestly, since this is just to hold you over until the uid/gid work I mentioned is done, it might just be easier to patch out the offending lines in your source.

Tried to build with snapcraft preload, but unfortunately, its current GIT state does not build:

Preparing to build snapcraft-preload 
Building snapcraft-preload 
cmake /root/build_cups/parts/snapcraft-preload/src -DCMAKE_INSTALL_PREFIX=
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/build_cups/parts/snapcraft-preload/build
make -j4
Scanning dependencies of target snapcraft-preload32
Scanning dependencies of target snapcraft-preload
[ 25%] Building CXX object CMakeFiles/snapcraft-preload32.dir/preload.cpp.o
[ 50%] Building CXX object CMakeFiles/snapcraft-preload.dir/preload.cpp.o
In file included from /usr/include/dirent.h:25:0,
                 from /root/build_cups/parts/snapcraft-preload/src/preload.cpp:24:
/usr/include/features.h:367:25: fatal error: sys/cdefs.h: No such file or directory
compilation terminated.
CMakeFiles/snapcraft-preload32.dir/build.make:62: recipe for target 'CMakeFiles/snapcraft-preload32.dir/preload.cpp.o' failed
make[2]: *** [CMakeFiles/snapcraft-preload32.dir/preload.cpp.o] Error 1
CMakeFiles/Makefile2:104: recipe for target 'CMakeFiles/snapcraft-preload32.dir/all' failed
make[1]: *** [CMakeFiles/snapcraft-preload32.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 75%] Linking CXX shared library libsnapcraft-preload.so
[ 75%] Built target snapcraft-preload
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2
Traceback (most recent call last):
  File "/usr/bin/snapcraft", line 9, in <module>
    load_entry_point('snapcraft==2.34', 'console_scripts', 'snapcraft')()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 542, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2569, in load_entry_point
    return ep.load()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2229, in load
    return self.resolve()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2235, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/lib/python3/dist-packages/snapcraft/cli/__main__.py", line 19, in <module>
    run(prog_name='snapcraft')
  File "/usr/lib/python3/dist-packages/click/core.py", line 716, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/core.py", line 696, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/click/core.py", line 1060, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/click/core.py", line 889, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/click/core.py", line 534, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/snapcraft/cli/lifecycle.py", line 132, in snap
    project_options, directory=directory, output=output)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lifecycle.py", line 349, in snap
    execute('prime', project_options)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lifecycle.py", line 130, in execute
    _Executor(config, project_options).run(step, part_names)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lifecycle.py", line 225, in run
    self._run_step(step, part, part_names)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lifecycle.py", line 262, in _run_step
    getattr(part, step)()
  File "/usr/lib/python3/dist-packages/snapcraft/internal/pluginhandler/__init__.py", line 330, in build
    self.plugin.build()
  File "/usr/lib/python3/dist-packages/snapcraft/plugins/cmake.py", line 82, in build
    self.make(env=env)
  File "/usr/lib/python3/dist-packages/snapcraft/plugins/make.py", line 107, in make
    self.run(command + ['-j{}'.format(self.parallel_build_count)], env=env)
  File "/usr/lib/python3/dist-packages/snapcraft/_baseplugin.py", line 202, in run
    return common.run(cmd, cwd=cwd, **kwargs)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/common.py", line 63, in run
    subprocess.check_call(['/bin/sh', f.name] + cmd, **kwargs)
  File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/bin/sh', '/tmp/tmplrnqj0qe', 'make', '-j4']' returned non-zero exit status 2
Stopping local:snapcraft-gently-finer-yeti
Traceback (most recent call last):
  File "/usr/bin/snapcraft", line 11, in <module>
    load_entry_point('snapcraft==2.34+17.10', 'console_scripts', 'snapcraft')()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 564, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2662, in load_entry_point
    return ep.load()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2316, in load
    return self.resolve()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2322, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/lib/python3/dist-packages/snapcraft/cli/__main__.py", line 19, in <module>
    run(prog_name='snapcraft')
  File "/usr/lib/python3/dist-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3/dist-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3/dist-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3/dist-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3/dist-packages/snapcraft/cli/lifecycle.py", line 188, in cleanbuild
    lifecycle.cleanbuild(project_options, remote)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lifecycle.py", line 326, in cleanbuild
    metadata=config.get_metadata(), remote=remote).execute()
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lxd.py", line 164, in execute
    raise e
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lxd.py", line 158, in execute
    self._container_run(command, cwd=self._project_folder)
  File "/usr/lib/python3/dist-packages/snapcraft/internal/lxd.py", line 113, in _container_run
    check_call(['lxc', 'exec', self._container_name, '--'] + cmd)
  File "/usr/lib/python3.6/subprocess.py", line 291, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['lxc', 'exec', 'local:snapcraft-gently-finer-yeti', '--', 'sh', '-c', 'cd /root/build_cups; snapcraft snap --output cups_0.1.0_amd64.snap']' returned non-zero exit status 1.

@till.kamppeter check my new pull request. I added you to snapcraft.yaml lines to comment all (f)chmod and (f)chown calls in cupsd. This is a bit brutal way to do things, but it does the job :slight_smile: Now if you install snap, it will run confined. There are still two denials, but those come on dbus targeted to avahi, where we need to review it and if this is genuine, we should probably update avahi-observe interface to pass this through.

audit: type=1107 audit(1510705184.258:5868): pid=1397 uid=107 auid=4294967295 ses=4294967295 msg='apparmor="DENIED" operation="dbus_method_call" bus="system" path="/" interface="org.freedesktop.DBus.Peer" member="Ping" mask="send" name="org.freedesktop.Avahi" pid=18475 label="snap.cups.cupsd" peer_pid=1413 peer_label="unconfined"

@jdstrand what do you think about this denial, shall we pass this call through?

@ondra, thank you very much. I have merged your pull request and built and installed the new snap. Now the snap’s cupsd starts and keeps running, but neither the print queues shared by the snap are discovered by the system nor the system’s queues by the snap. Seems to be problems with Avahi or D-Bus here (CUPS and cups-browsed communicate with avahi-daemon via D-Bus).

After running

sudo snap connect cups:avahi-control

and rebooting the virtual machine the auto discovery of the shared printers works again, in both directions. So after manually connecting Avahi the CUPS/Avahi/D-Bus/cups-browsed realm seems to fully work with a confined snap.

Can I reach the same goal also without rebooting the VM? How do I restart daemons within a snap?

We also need a way to connect the Avahi interface automatically.

For CUPS <-> Avahi and cups-browsed <-> Avahi communication only the D-Bus within the snap’s confinement (and perhaps also through the avahi-control interface) is needed, and so Avahi use seems to work without D-Bus denials.
The denials are perhaps from notification which CUPS sends out to tell about events as queue or job creation, job finishing, … These being denied should not prevent CUPS from working, but there will be no printing-related desktop notifications.

@jdstrand ignore this comment please. Clearly it was long day for me and I forgot to connect avahi interface, so it was legitimate denial.

Did some first tests:

  • cups.lp <file> does not work, one has to use cat <file> | cups.lp. lp would need access to home directory.
  • Administrative operations, like cups.cancel -a need sudo. No privileged user or group for administration.
  • sudo cups.cupsctl tries to access /etc/cups/cupsd.conf instead of the one of the snap. Is actually the cupsctl executable of the snap executed here? Or the one of the system?
  • Printing does not work, filters and backends stop with ... stopped with status 101 (Operation not permitted). Filters/backends need to be investigated.
  • Web interface on http://localhost:10631 can get accessed with browser running on the VM, but any of its pages (admin, printers, classes, …) gives an “Internal Server Error”.

I know you moved on from snapcraft-preload, but I suspect you are missing a build or stage packages. @sergiusens or @Trevinho, can you look at this, perhaps updating any relevant documentation?

Make a request for auto-connection following Process for aliases, auto-connections and tracks

The stopped with status 101 (Operation not permitted) problem of the filters is probably caused by the fact that CUPS runs these as the user “lp” which actually exists in the system (at least in the classic one) but as we have patched out all chown/chmod operations all job files in the spool area are owned by user root and group root and being only group-readable and not world-readable the filters cannot access the job data.
CUPS needs to be patched so that the “lp” user and group is actually root (ID = 0).

The CGI programs of the web interface also need to be checked whether CUPS runs them as root and not as an unprivileged system user.

CUPS internally sets the the PATH environment variable to <CUPS filter dir>:/bin:/sbin:/bin:/usr/bin, we need it to take the PATH variable of its environment and add the filter dir to that, patch is probably similar to the one which we already apply for LD_LIBRARY_PATH. This is needed for filters to find helper utilities.