krellian-kiosk is a snap which provides a web runtime for digital signage and interactive kiosks and therefore needs to be both a daemon and have browser-support. Krellian Kiosk is also intended to become an Ubuntu Appliance.
Requesting approval for daemon + browser-support for the krellian-kiosk snap.
Does krellian-kiosk absolutely require the use of browser-support? I understand the wish for daemon is to have long-lived daemon that is automatically started etc - in that case, perhaps the use of the snap_daemon user via system-usernames could help so that the snap doesnât have to run as root.
However, even in this case, the daemon will still be started as root and it would have to drop privileges to the snap-daemon user, so this doesnât entirely alleviate the security concern.
As such, if this browser-support is absolutely required, we would need to perform publisher vetting as though this were a request for classic confinement.
Krellian Kiosk acts as both a web client and web server. It is essentially a full screen web browser which can be remotely controlled over the Web of Things. I therefore canât see any way around it being both a browser and a daemon as that is fundamentally what itâs designed to do.
Running as a non-root user would be great, but the application also needs to configure network settings during first time setup (via network-manager) and bind a web server to ports 80 and 443 which I expect requires root access. (It might be possible to bind to different ports and redirect traffic using iptables, but that probably requires root access to configure post-boot as well).
What does publisher vetting involve exactly?
Thanks
Ben
P.S. Might it make sense to request auto-connection of the network-manager interface at the same time?
Are you saying that the browser itself is binding to port 80/443 and manipulating the firewall? If so, the typical design pattern would be to perform all your root actions early, then permanently drop to an unprivileged user. See System usernames for example code on how to do this in a snap-compliant manner.
You might also consider separating your application into different parts which adds security benefits. I donât know how feasible that is, but eg:
run your webserver as a separate daemon, and have it bind to port 80/443 and permanently drop to snap_daemon
run your web browser as a separate daemon and either permanently drop immediately to snap_daemon or start is as snap_daemon
use configure hooks/etc to setup your firewall settings or have a separate daemon modify the firewall settings
It sounds like your snap would need to plugs network, network-bind and firewall-control (and perhaps network-control).
For use of âbrowser-supportâ with a daemon, your snap should be modified to use system-usernames so that the browser process is not running as root. Once that is done, weâll ask that you continue to use system-usernames for the browser process and assuming you agree, a member of our advocacy will contact you privately and ask a few questions related to your relationship with the software, device, etc.
Iâm saying that the application acts as both a web user agent (browser) and web server and the server part binds to port 80/443. As long as the application can bind to port 80/443 (as it currently does) I donât think it should need to configure the firewall.
It does need to configure the network (e.g. configure a Wi-Fi hotspot, broadcast a service over mDNS and connect to a Wi-Fi access point during first time setup) which can hopefully all still be done as a non-root user via the network-manager interface? (It may also need to re-configure network settings again in future if the network drops for any reason).
OK, I will try to get my head around that, thank youâŠ
I would be open to that, but Iâm not sure how feasible it is or how much it would actually help in practice.
An example feature of the application is that a user can control the kiosk remotely using its web interface to tell it to load a given URL:
Turning the web user agent and web server into separate executables inside the snap package would mean adding an additional IPC layer for the server process to communicate with the user agent process in order to control it. An example IPC mechanism might be a WebSocket server, which would just be recreating what already exists and adding an unnecessary extra layer of complexity!
If Iâve understood correctly then I would rather avoid this extra complexity if possible as it isnât needed for any other platforms on which the kiosk application might run.
So for now I will try to implement the system usernames approach and let you know how I get on. I am of course happy to answer any questions about my company and product to assist with the approval process.
@benfrancis please update us on your progress with system-usernames etc - also I assume you still require both daemon and browser-support for krellian-kiosk - if so, the only option then is to do publisher vetting as for classic confinement requests. If this is the case, let me know and we can get the @advocacy team to start the process.
Hi @alexmurray@msalvatore, thanks for following up. I havenât made any tangible progress on this issue yet.
I havenât fully explored the system usernames system yet, but I noticed all the examples were in C so I was looking for advice on how to achieve this for an Electron/Node.js application.
A key issue is that the application needs to bind a web server to port 80/443 which requires root access.
@ijohnson has suggested having the application start up using the snap_daemon user using setpriv, then have systemd bind to port 80 and add listen-streams to snapcraft.yaml which connect up the sockets.
This seems non-trivial to figure out (though similar to the iptables solution I suggested above) so Iâve been focusing on implementing the remaining features for the 0.1 release of the snap (e.g. configuring a Wi-Fi hotspot for first boot) to make sure that binding to port 80/443 is definitely the only feature that needs root, before I do that.
Any more advice you can provide on how to achieve the above with systemd and listen-stream would be helpful.
Sorry if my email was unclear, but I think this should be rather straight-forward for your application, wherever you open the port(s), just replace that bit of code with the calls to get a port from systemd. The relevant bit of code from that example is just
var server = http.createServer(function(req, res) {
...
}
server.listen('systemd')
Wherever you normally call server.listen(80) or some such, just use systemd instead.
and the associated listen-streams in your snapcraft.yaml:
@benfrancis - another gentle ping, just to let you know this is still on our radar, please donât forget to update us on your progress when you get a chance to look at this further.
Apologies for the extended delay, the WebThings transition project I was working on took over for a while there!
@ijohnson Iâve had a stab at your proposed solution here. Can you tell me whether Iâm going in the right direction?
My application already accepts a port number as a command line argument so it was easy to use a special port for the snap package by changing it from â80â to âsystemdâ in the launch command. Forgive my ignorance, but is âsystemdâ a magic value that Node.js is supposed to know about, or is there some other setup required for this to work?
The snap with the above configuration builds successfully but it wonât run. The logs are here.
Some lines that look like they might be relevant:
Dec 17 14:23:46 Unknown-b8-27-eb-f2-7b-0b krellian-kiosk.krellian-kiosk[3085]: _XSERVTransSocketCreateListener: failed to bind listener
Dec 17 14:23:46 Unknown-b8-27-eb-f2-7b-0b krellian-kiosk.krellian-kiosk[3085]: _XSERVTransSocketUNIXCreateListener: ...SocketCreateListener() failed
Dec 17 14:23:46 Unknown-b8-27-eb-f2-7b-0b krellian-kiosk.krellian-kiosk[3085]: _XSERVTransMakeAllCOTSServerListeners: failed to create listener for local
Dec 17 14:23:11 Unknown-b8-27-eb-f2-7b-0b krellian-kiosk.krellian-kiosk[2570]: /snap/krellian-kiosk/x2/bin/xwayland-kiosk-launch: line 151: /snap/krellian-kiosk/x2/bin/drop-snap-daemon.sh: Permission denied
Dec 17 14:23:47 Unknown-b8-27-eb-f2-7b-0b krellian-kiosk.krellian-kiosk[3085]: /snap/krellian-kiosk/x2/bin/xwayland-kiosk-launch: line 151: /snap/krellian-kiosk/x2/bin/drop-snap-daemon.sh: Permission denied
Hi again @benfrancis are you able to get system journal denials as well from journalctl --no-pager | grep DENIED ?
Iâm curious what the denials are, as it appears to be trying to listen on the local socket but failing due to EPERM? Or perhaps the drop-snap-daemon.sh file is not executable ?
Also did you add the util-linux package to your stage-packages so that the setpriv utility is available from your snap?
Maybe this was missed somewhere along the way, but you also need to specify require('systemd'); in your javascript somewhere, as thatâs the package which will inherit the ports from systemd via listen-streams, etc.
this denial (and the others except the fontconfig one) are interesting but doesnât seem to be related to inheriting the http socket from systemd, these errors seem to be related to your app trying to use XWayland things and not binding to the port that systemd is providing, does your snap work if you install it in devmode?
Also if you just use the systemd port specification but not drop privileges to snap_daemon user does it work? My guess is that your snap 1) works in devmode and 2) works when run as root, but not as snap_daemon because it seems that the snap is trying to do XWayland things which afaict need to be done as root.