Request: CUPS Snap (“cups”) auto connection to of cups:cups-control to cups:admin and also of the network-manager-observe interface

Yes, the cups plug should also work with no CUPS Snap installed. It should work the same way as cups-control, being implicit on classic systems. From the Snap Store auto-connection side cups should auto-connect by default and cups-control should only auto-connect if the Snap Store team has approved the Snap requesting this. Existing Snaps which plug cups-controlfor the sole sake of printing should switch to cups.

Your slots-per-plug: * idea should even work, as the client app will connect to CUPS through the standard resources, /run/cups/cups.sock and localhost:631. If only the CUPS Snap is installed, these resources are used by the CUPS Snap, if a classic CUPS is installed, independent whether there is the CUPS Snap in addition, these resources lead to the classic CUPS. So if the app plugs both slots, the communication always goes to the correct, expected CUPS. And both CUPS are protected against administrative tasks from fully confined Snaps plugging only cups.

So let us go the slots-per-plug: * way. Do I have to change something in the CUPS Snap for that and if yes, tell me what. And tell me if I have to change it immediately or wait for some change in snapd or so.

Right. The problem I was alluding to was what happens when there are two candidates on the system for auto-connection (system:cups and cups:printing): at present, I think this results in neither slot being connected. That’s what the slots-per-plug assertion can help with.

Se my post above again, I have completed it now.

In this case both slots should be connected, then all should work as I described in my previous post.

So then we should simply use a slots-per-plug: * for both cups and cups-control for both connect and auto-connect. As there is only one cupsd on the standard resources (if at least one instance of CUPS is installed) the correct thing will happen when the user application’s plug plugs both the system’s slot and the slot of the CUPS Snap.

Is this how slots-per-plug is implemented in current snapd?

Where have the slots-per-plug: * declarations to be done?

slots-per-plug is something that would go in the snap declaration published by the store. It is not something you can control from your snapcraft.yaml file. This is the last time I made a request for such a declaration:

Back then, there was no policy for granting this type of request, so it was handled in an ad-hoc fashion. I’m not sure what the status is now.

Now for some other questions:

  1. You’ve got both plugs and slots for the cups and cups-control interfaces, with the plugs taking the default name, and the slots named printing and admin respectively. Given that Most other snaps will generally be dealing with the CUPS snap’s slots, might it make more sense to give the slots the default names?

    With things like it is, it seems likely that people will try commands like sudo snap connect app:cups-control cups:cups-control and then get confused about them not working.

  2. Does your snap actually need a cups plug? I would have thought the utility commands would have enough access to the CUPS socket through the cups-control slot.

  3. I can see the cups-control plug as being necessary in order for the snapctl is-connected checks to pass and allow administrative commands like lpadmin to do their thing.

    I wonder if we could solve this by adding one more exit code to the is-connected command representing “not connected, but from the same snap”. That might be enough to get the bundled admin utilities to function without the plug. What do you think?

  1. Yes, I can do that. I simply did not know that it is possible and the default. If I do so, do we need to re-vote or is it simply understood?
  2. OK, as the permissions of a plug a given by-Snap and not by-app (one app plugs cups-control so all the other apps plugging only cups are also allowed to admin) I can let them all plug cups-control and so the CUPS Snap does not plug cups any more.
  3. This is a good idea. Please implement it. But as it takes time for you to implement and for the mainyainers to approve the @reviewers should for now apply the auto connections as they are and I modify the CUPS Snap as soon as this new exit code is available.

@reviewers, is it OK if I rename the slots of the CUPS Snap to standard?

From

slots:
  # Provide the cups-control and cups slots for other snaps to connect to
  admin:
    interface: cups-control
  printing:
    interface: cups

to

slots:
  # Provide the cups-control and cups slots for other snaps to connect to
  cups-control:
    interface: cups-control
  cups:
    interface: cups

so that we now need the following auto-connections:

sudo snap connect cups:cups-control cups:cups-control
sudo snap connect cups:cups cups:cups
sudo snap connect cups:network-manager-observe

Does it work this way? Does it need a new voting? Or can I simply do this change right now and you apply the auto-connects this way in the Snap Store?

Could you apply the auto-connects this way then, set slots-per-plug: * for connecting and auto-connecting both cups and cups-control for user applications uploaded into the Snap Store, and let the Snap Store allow auto-connection of cups for uploaded user applications without explicit approval?

@jamesh, If I do the renaming of the slots (printing -> cups, admin -> cups-control) as described in my previous post and build the Snap I get the following error:

Snapping |                                                           
Failed to create snap, snap command failed:
stdout:

stderr:
error: cannot pack "/root/prime": cannot validate snap "cups": cannot have plug and slot with the same name: "cups-control"

We would appreciate it if you anonymously reported this issue.
No other data than the traceback and the version of snapcraft in use will be sent.

How do I do this correctly?

Plugs and slots share the same namespace, as that error message indicates. I was suggesting that you rename the plugs (which are only of interest to the cups snap) to something else, so the slots could use the default names.

So you mean the plug(s) for the command line tools which come with the Snap? So for example I use only one universal plug for the command line tools, like cups-internal and let this get auto-connected to the slot cups:cups-control via auto-connect? User applications then plug their cups to cups:cups and their cups-control to cups:cups-control?

This would mean that I also have to modify CUPS so that it accepts admin requests also on cups:cups-internal? Or can I modify CUPS to accept admin requests simply if the SNAP NAME (not the interface plugged) is “cups” and then the tools do not need to plug anything at all?

And if I have to use cups-internal for the CUPS tools does this not need to extend the design of snapd to support this plug?

@jamesh, @reviewers:

I got it working now:

First I re-defined plugs and slots in snapcraft.yaml:

plugs:
  cups-internal:
    interface: cups-control

slots:
  # Provide the cups-control and cups slots for other snaps to connect to
  cups-control:
    interface: cups-control
  cups:
    interface: cups

I set the slots of cupsd as follows:

apps:
  cupsd:
    [...]
    slots: [cups-control, cups]

On all other apps I replaced the plugs cups and cups-control by cups-internal:

  cups-browsed:
    [...]
    plugs: [network, network-bind, network-manager-observe, avahi-control, cups-internal]
  lpinfo:
    command: sbin/lpinfo
    plugs: [network, cups-internal]
  lpadmin:
    command: sbin/lpadmin
    plugs: [network, avahi-control, home, cups-internal]
  lpstat:
    command: bin/lpstat
    plugs: [network, avahi-control, cups-internal]
  [...]

I build CUPS with a configure option to call the cupsctl API call with cups-control as slot name:

parts:
  [...]
  cups:
    [...]
    configflags:
      [...]
      - --with-cups-control-slot=cups-control
      [...]

Now I change the connection I requested in this thread to

sudo snap connect cups:cups-internal cups:cups-control
sudo snap connect cups:network-manager-observe

and for external utilities (example) I do the following connections:

sudo snap connect cups-admin-test:cups-control cups:cups-control
sudo snap connect cups-user-app-test:cups cups:cups

Now it all works as expected.

@reviewers:

This means:

This request should be now considered as that following auto-connections are requested:

sudo snap connect cups:cups-internal cups:cups-control
sudo snap connect cups:network-manager-observe

The auto-connections are internally doing the same as the original ones, the interfaces are only renamed.

It must be assured that a Snap of a user application which prints and is put into the Snap Store should auto-connect its cups plug to BOTH :cups AND cups:cups on installation. slots-per-plug: * should be used at the right place for that.

Same for Snaps of printer managers using cups-control with the difference that here explicit permission should be needed for auto-connection. Also here slots-per-plug: * at the right place should be used.

@reviewers: I will commit my above-mentioned changes now, could you apply the changes as I described here? Thanks in advance.

@reviewers, @jamesh, Changes for new slot names cups:cups and cups:cups-control are committed to the CUPS Snap now. The Snap Store is rebuilding it currently.

Here is a PR adding the extra snapctl is-connected exit code:

And here’s one that adds an implicit system:cups slot:

@jamesh, thank you very much for your pull requests.

@jamesh, one question, can the client-is-from-the-same-Snap case not be checked much more easily? Keeping in mind that the Snap Store can only hold one Snap named “cups” and on a system there also can be only one Snap called “cups” be installed, CUPS could identify inquiries from the same snap by the AppArmor context? For CUPS it starts always with snap.cups. and for other Snaps it never starts with snap.cups..

Yes, you could interpret the snap name in the AppArmor label yourself as a temporary measure. If that lets you get rid of the cups-control plug on the snap, I’d do that: if you can get by without the self connections, it should make your snap a bit more robust.

@reviewers, as it will take time until @jamesh’s pull requests will get implemented in snapd and the CUPS Snap is working as it is now, I ask you to apply the auto-connections of interfaces and the slots-per-plug: * as I described in post #31 to make the CUPS Snap working in a user-friendly way right now. Thanks in advance.

@jamesh, with the check of the AppArmor label I can free myself from the requirement that the Snap Store team has to approve the self connection as auto connection. Independent of this, the utilities have to plug one of cups or cups-control as otherwise AppArmor blocks their access to the CUPS Socket, even if the utilities belong to the same Snap, and I get a lpstat: Bad file descriptor. I have quickly tested it right now, simply removing the cups-internal plug from lpstat and lpinfo in snapcraft.yaml.

So the only way to get access to the socket at all for the utilities is to create the cups-internal alias to the cups-control plug as I am using right now (see my longer post above). This is independent of the method how to allow administrative inquiries, checking the AppArmor label (or adding another exit code to snapctl) only reduces the number of auto-connections to be approved by the Snap Store team.

@jamesh, in the second pull request, in interfaces/builtin/cups.go you have deny-auto-connection: true. Should that not be false as for simple printing (no admin) we want to auto-connect by default. Am I right?

Re. the “bad file descriptor” error, Try removing the “slots:” stanza from the cupsd app definition in your snapcraft.yaml. This will cause those slots to be associated with every app of the snap, which should give the utilities access to the slot.

Re. auto-connection of the cups interface, this matches the current policy of cups-control. It seemed easier to propose just one change at a time.