jupyterhub / systemdspawner

Spawn JupyterHub single-user notebook servers with systemd
BSD 3-Clause "New" or "Revised" License
92 stars 45 forks source link

Nonroot #40

Closed behrmann closed 1 year ago

behrmann commented 6 years ago

Hi,

thanks a lot for this spawner, here are a couple of additions I made this week and that I'd like to share.

I wasn't too happy to have the jupyterhub run as root, so this adds a spawner that relies on service files that are installed on the system. This obviates the need for most of the config that SystemdSpawner has, since those options move to the service file.

The jupyterhub service runs as system user jupyter (or whatever you configure) and can either start the user instances via sudo, which needs a rule along the lines of

Cmnd_Alias JUPYTERSTART = /usr/bin/systemctl start jupyter-singleuser@[[\:alpha\:]]?[[\:alnum\:]]*.service
Cmnd_Alias JUPYTERSTOP = /usr/bin/systemctl stop jupyter-singleuser@[[\:alpha\:]]?[[\:alnum\:]]*.service
Cmnd_Alias JUPYTERRESET = /usr/bin/systemctl reset-failed jupyter-singleuser@[[\:alpha\:]]?[[\:alnum\:]]*.service
Defaults!JUPTERSTART !requiretty
Defaults!JUPTERSTOP !requiretty
Defaults!JUPTERRESET !requiretty
jupyter HOST = (root) NOPASSWD: JUPYTERSTART
jupyter HOST = (root) NOPASSWD: JUPYTERSTOP
jupyter HOST = (root) NOPASSWD: JUPYTERRESET

or plain systemctl using a policykit rule that could look like this

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.systemd1.manage-units") {
        if (action.lookup("unit").match(/^jupyter-singleuser@[a-zA-Z]?\w*.service$/)) {
            var verb = action.lookup("verb");
            if ((verb == "start" || 
                 verb == "stop" || 
                 verb == "reset-failed") && 
                subject.user == "jupyter") {
                return polkit.Result.YES;
            }
        }
    }
});

I have tested both cases, but the latter requires a policykit version of 106 or later and Debian (and its derivatives like Ubuntu) only ship a heavily patched policykit 105 and require the sudo way. The sudo way also precludes the use of NoNewPriviliges for the jupyterhub service.

To get the arguments from the jupyterhub server to the single user instances, I use an environment file. Since it contains sensitive information and therefore cannot be world readable, I use ACLs to make it readable for the user of the instance, since only root can chown.

So far I've tested this on Debian Sid (unstable), were it works fine, but it lacks documentation and is still geared towards my use case.

So, what needs to be done to get this upstreamed (if there is interested from your side)? :)

behrmann commented 6 years ago

I just updated a couple of small issues and a bug, when stopping user servers, that I just noticed today.

It would be wonderful if I could get some feedback on whether and if so how, this could be merged. Thanks in advance!

behrmann commented 5 years ago

I just rebased on the current master and cleaned this up a little.

behrmann commented 5 years ago

I recently got around to also testing on a reasonable version of policykit and updated this accordingly and also added some documentation.