jupyterhub-staticsystemdspawner 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jupyterhub_staticsystemdspawner-0.1.0/.github/workflows/pypi-publish.yml +72 -0
- jupyterhub_staticsystemdspawner-0.1.0/.gitignore +89 -0
- jupyterhub_staticsystemdspawner-0.1.0/CHANGELOG.md +5 -0
- jupyterhub_staticsystemdspawner-0.1.0/LICENSE +30 -0
- jupyterhub_staticsystemdspawner-0.1.0/PKG-INFO +363 -0
- jupyterhub_staticsystemdspawner-0.1.0/README.md +351 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/PKG-INFO +363 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/SOURCES.txt +21 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/dependency_links.txt +1 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/entry_points.txt +6 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/requires.txt +2 -0
- jupyterhub_staticsystemdspawner-0.1.0/jupyterhub_staticsystemdspawner.egg-info/top_level.txt +1 -0
- jupyterhub_staticsystemdspawner-0.1.0/pyproject.toml +48 -0
- jupyterhub_staticsystemdspawner-0.1.0/setup.cfg +4 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/__init__.py +5 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/resources/defaultserver.service +17 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/resources/hardening.conf +8 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/resources/namedserver.service +17 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/resources/slice.conf +5 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/staticsystemdspawner.py +287 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/systemd.py +99 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/unitgenerator.py +136 -0
- jupyterhub_staticsystemdspawner-0.1.0/staticsystemdspawner/wrapper.py +43 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
name: Build and publish Python package to PyPI or TestPyPI
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build-wheels-and-sdist:
|
|
7
|
+
name: Build wheels and sdist
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- name: Fetch sources
|
|
11
|
+
uses: actions/checkout@v7
|
|
12
|
+
with:
|
|
13
|
+
fetch-depth: 0
|
|
14
|
+
persist-credentials: false
|
|
15
|
+
|
|
16
|
+
- name: Set up Python 3.13
|
|
17
|
+
uses: actions/setup-python@v7
|
|
18
|
+
with:
|
|
19
|
+
python-version: '3.13'
|
|
20
|
+
|
|
21
|
+
- name: Build a binary wheel and a source tarball
|
|
22
|
+
run: pipx run build --sdist --wheel --outdir dist/ .
|
|
23
|
+
|
|
24
|
+
- uses: actions/upload-artifact@v7
|
|
25
|
+
with:
|
|
26
|
+
name: cibuilds
|
|
27
|
+
path: |
|
|
28
|
+
dist/*.tar.gz
|
|
29
|
+
dist/*.whl
|
|
30
|
+
|
|
31
|
+
publish-to-testpypi:
|
|
32
|
+
needs: [build-wheels-and-sdist]
|
|
33
|
+
name: Upload build to TestPyPI
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
environment:
|
|
36
|
+
name: testpypi
|
|
37
|
+
url: https://test.pypi.org/p/jupyterhub-staticsystemdspawner
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write
|
|
40
|
+
steps:
|
|
41
|
+
- name: Downloads build wheels and sdist
|
|
42
|
+
uses: actions/download-artifact@v8
|
|
43
|
+
with:
|
|
44
|
+
pattern: cibuilds
|
|
45
|
+
path: dist
|
|
46
|
+
merge-multiple: true
|
|
47
|
+
|
|
48
|
+
- name: Publish package to TestPyPI
|
|
49
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
|
+
with:
|
|
51
|
+
repository-url: https://test.pypi.org/legacy/
|
|
52
|
+
|
|
53
|
+
build-and-publish-to-pypi:
|
|
54
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
55
|
+
needs: [build-wheels-and-sdist]
|
|
56
|
+
name: Upload release to PyPI
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment:
|
|
59
|
+
name: pypi
|
|
60
|
+
url: https://pypi.org/p/jupyterhub-staticsystemdspawner
|
|
61
|
+
permissions:
|
|
62
|
+
id-token: write
|
|
63
|
+
steps:
|
|
64
|
+
- name: Downloads build wheels and sdist
|
|
65
|
+
uses: actions/download-artifact@v8
|
|
66
|
+
with:
|
|
67
|
+
pattern: cibuilds
|
|
68
|
+
path: dist
|
|
69
|
+
merge-multiple: true
|
|
70
|
+
|
|
71
|
+
- name: Publish package to PyPI
|
|
72
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
env/
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
# Usually these files are written by a python script from a template
|
|
29
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.coverage
|
|
41
|
+
.coverage.*
|
|
42
|
+
.cache
|
|
43
|
+
nosetests.xml
|
|
44
|
+
coverage.xml
|
|
45
|
+
*,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
|
|
48
|
+
# Translations
|
|
49
|
+
*.mo
|
|
50
|
+
*.pot
|
|
51
|
+
|
|
52
|
+
# Django stuff:
|
|
53
|
+
*.log
|
|
54
|
+
local_settings.py
|
|
55
|
+
|
|
56
|
+
# Flask stuff:
|
|
57
|
+
instance/
|
|
58
|
+
.webassets-cache
|
|
59
|
+
|
|
60
|
+
# Scrapy stuff:
|
|
61
|
+
.scrapy
|
|
62
|
+
|
|
63
|
+
# Sphinx documentation
|
|
64
|
+
docs/_build/
|
|
65
|
+
|
|
66
|
+
# PyBuilder
|
|
67
|
+
target/
|
|
68
|
+
|
|
69
|
+
# IPython Notebook
|
|
70
|
+
.ipynb_checkpoints
|
|
71
|
+
|
|
72
|
+
# pyenv
|
|
73
|
+
.python-version
|
|
74
|
+
|
|
75
|
+
# celery beat schedule file
|
|
76
|
+
celerybeat-schedule
|
|
77
|
+
|
|
78
|
+
# dotenv
|
|
79
|
+
.env
|
|
80
|
+
|
|
81
|
+
# virtualenv
|
|
82
|
+
venv/
|
|
83
|
+
ENV/
|
|
84
|
+
|
|
85
|
+
# Spyder project settings
|
|
86
|
+
.spyderproject
|
|
87
|
+
|
|
88
|
+
# Rope project settings
|
|
89
|
+
.ropeproject
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-2024, Project Jupyter Contributors
|
|
4
|
+
2024-2026, Jörg Behrmann
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
11
|
+
list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
* Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jupyterhub-staticsystemdspawner
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: JupyterHub Spawner using systemd unit files
|
|
5
|
+
Author-email: Jörg Behrmann <behrmann@physik.fu-berlin.de>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: jupyterhub>=0.9
|
|
10
|
+
Requires-Dist: tornado>=5.0
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# staticsystemdspawner
|
|
14
|
+
|
|
15
|
+
The **staticsystemdspawner** enables JupyterHub to spawn single-user notebook
|
|
16
|
+
servers using the [systemd](https://www.freedesktop.org/wiki/Software/systemd/)
|
|
17
|
+
service manager, that is available on all current major Linux distributions.
|
|
18
|
+
|
|
19
|
+
This project is a friendly fork of [systemdspawner](https://github.com/jupyterhub/systemdspawner).
|
|
20
|
+
|
|
21
|
+
## How is this different from systemdspawner?
|
|
22
|
+
|
|
23
|
+
systemdspawner uses `systemd-run` to start transient units that are constructed
|
|
24
|
+
on the fly by systemdspawner itself. This makes it necessary for JupyterHub to
|
|
25
|
+
be able to use `systemd-run`.
|
|
26
|
+
|
|
27
|
+
staticsystemdspawner instead uses pre-generated unit files that only need to be
|
|
28
|
+
started, which makes the scope of what to allow JupyterHub to do smaller and
|
|
29
|
+
also allows to customise the units to a much greater degree without the need to
|
|
30
|
+
pipe all systemd options through the spawner.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
The features of staticsystemdspawner are broadly similar to those of
|
|
35
|
+
systemdspawner, but very open-ended. If you can think of it, you can probably
|
|
36
|
+
hack it up without having to touch the code of systemdspawner.
|
|
37
|
+
|
|
38
|
+
You can use everything in the systemd suite, which gets you all sorts of
|
|
39
|
+
sandboxing and resource control options, see the man pages
|
|
40
|
+
- `man systemd.service`,
|
|
41
|
+
- `man systemd.exec`, and
|
|
42
|
+
- `man systemd.resource-control`
|
|
43
|
+
for details and `man systemd.directives` if need an index.
|
|
44
|
+
|
|
45
|
+
## Requirements
|
|
46
|
+
|
|
47
|
+
### Systemd
|
|
48
|
+
|
|
49
|
+
staticsystemdspawner requires you to use a Linux Distro that ships with at least
|
|
50
|
+
systemd v252, which shipped by default in Debian bookworm (released in 2023),
|
|
51
|
+
but is also available in Debian bullseye (released in 2021) via Debian
|
|
52
|
+
backports. It might also work with systemd v247 available in Debian bullseye,
|
|
53
|
+
but this has not been tested, we recommend using at least version v252.
|
|
54
|
+
|
|
55
|
+
Consult `systemctl --version` for your version information.
|
|
56
|
+
|
|
57
|
+
### Polkit
|
|
58
|
+
|
|
59
|
+
To control unit lifecycle staticsystemdspawner uses polkit. A version larger
|
|
60
|
+
than polkit 105 is needed. These are available in Debian starting with Debian
|
|
61
|
+
bookworm or from the experimental repositories before that.
|
|
62
|
+
|
|
63
|
+
### Local unit files for all users
|
|
64
|
+
|
|
65
|
+
You will have to add local unit files for all users.
|
|
66
|
+
|
|
67
|
+
### Linux Distro compatibility
|
|
68
|
+
|
|
69
|
+
Anything with a recent systemd (v252 or newer) and polkit (newer than 105),
|
|
70
|
+
should work, i.e. all Debian versions starting with Debian bookworm, all Ubuntu
|
|
71
|
+
versions starting with Ubuntu 24.04 LTS and all versions of RHEL and its
|
|
72
|
+
derivatives starting with RHEL9 should work out of the box
|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
You cannot install it from PyPI yet, but need to install it from git
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install git+https://<url to the repo>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
You can enable it for your jupyterhub with the following lines in your
|
|
83
|
+
`jupyterhub_config.py` file
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
c.JupyterHub.spawner_class = 'staticsystemdspawner.StaticSystemdSpawner'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Note that to confirm systemdspawner has been installed in the correct jupyterhub
|
|
90
|
+
environment, a newly generated config file should list `staticsystemdspawner` as one of the
|
|
91
|
+
available spawner classes in the comments above the configuration line.
|
|
92
|
+
|
|
93
|
+
The Hub itself can be run as an unprivileged user
|
|
94
|
+
```ini
|
|
95
|
+
# jupyterhub.service
|
|
96
|
+
[Unit]
|
|
97
|
+
Description=Jupyterhub
|
|
98
|
+
After=network.target postgresql.service jupyterhub-httpproxy.service
|
|
99
|
+
Wants=network.target postgresql.service
|
|
100
|
+
PartOf=jupyterhub.target
|
|
101
|
+
|
|
102
|
+
[Service]
|
|
103
|
+
User=jupyter
|
|
104
|
+
Environment="PATH=/opt/jupyter/hubenv/bin:/usr/bin:/usr/local/bin:/bin"
|
|
105
|
+
ConfigurationDirectory=jupyterhub
|
|
106
|
+
RuntimeDirectory=jupyterhub
|
|
107
|
+
StateDirectory=jupyterhub
|
|
108
|
+
|
|
109
|
+
LoadCredentialEncrypted=jupyterhub.dbpassword
|
|
110
|
+
LoadCredentialEncrypted=jupyterhub.authcred
|
|
111
|
+
LoadCredentialEncrypted=jupyterhub.cookiesecret
|
|
112
|
+
LoadCredentialEncrypted=jupyterhub.proxy.authtoken
|
|
113
|
+
|
|
114
|
+
ExecStart=/opt/jupyter/hubenv/bin/jupyterhub --config ${CONFIGURATION_DIRECTORY}/jupyterhub_config.py
|
|
115
|
+
|
|
116
|
+
PrivateTmp=yes
|
|
117
|
+
NoNewPrivileges=yes
|
|
118
|
+
ProtectSystem=strict
|
|
119
|
+
ProtectHome=yes
|
|
120
|
+
WorkingDirectory=/var/lib/jupyterhub
|
|
121
|
+
```
|
|
122
|
+
and it is recommended to start it as a part of a target, e.g.
|
|
123
|
+
```ini
|
|
124
|
+
[Unit]
|
|
125
|
+
Description=JupyterHub API services
|
|
126
|
+
Wants=jupyterhub.service jupyterhub-httpproxy.service nginx.service
|
|
127
|
+
|
|
128
|
+
[Install]
|
|
129
|
+
WantedBy=multi-user.target
|
|
130
|
+
```
|
|
131
|
+
assuming the existence of a separate `jupyterhub-httpproxy.service` and running the Hub behind Nginx as a proxy.
|
|
132
|
+
|
|
133
|
+
### Aside: Running the configurable http proxy as dynamic user
|
|
134
|
+
|
|
135
|
+
```ini
|
|
136
|
+
# jupyterhub-httpproxy.service
|
|
137
|
+
[Unit]
|
|
138
|
+
Description=JupyterHub HTTP proxy
|
|
139
|
+
After=network-online.target
|
|
140
|
+
PartOf=jupyterhub.target
|
|
141
|
+
|
|
142
|
+
[Service]
|
|
143
|
+
User=jupyterproxy
|
|
144
|
+
DynamicUser=yes
|
|
145
|
+
|
|
146
|
+
LoadCredentialEncrypted=jupyterhub.proxy.authtoken
|
|
147
|
+
ExecStart=/opt/jupyter/configurable-http-proxy-wrapper \
|
|
148
|
+
--ip 127.0.0.1 \
|
|
149
|
+
--port 8000 \
|
|
150
|
+
--api-ip 127.0.0.1 \
|
|
151
|
+
--api-port 8001 \
|
|
152
|
+
--host-routing \
|
|
153
|
+
--default-target=http://127.0.0.1:8081 \
|
|
154
|
+
--error-target=http://127.0.0.1:8081/hub/error
|
|
155
|
+
|
|
156
|
+
SuccessExitStatus=0 2
|
|
157
|
+
|
|
158
|
+
PrivateTmp=yes
|
|
159
|
+
ProtectSystem=strict
|
|
160
|
+
ProtectHome=yes
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Polkit
|
|
164
|
+
|
|
165
|
+
To authorize JupyterHub to start and stop units a polkit rule is used.
|
|
166
|
+
```js
|
|
167
|
+
polkit.addRule(function(action, subject) {
|
|
168
|
+
if (action.id == "org.freedesktop.systemd1.manage-units") {
|
|
169
|
+
polkit.log("action=" + action);
|
|
170
|
+
polkit.log("subject=" + subject);
|
|
171
|
+
if (action.lookup("unit").match(/^jupyterhub-singleuser-[a-z][a-z0-9]{0,30}.service$/) ||
|
|
172
|
+
action.lookup("unit").match(/^jupyterhub-singleuser-[a-z][a-z0-9]{0,30}@[a-zA-Z0-9_\\]+.service$/)) {
|
|
173
|
+
var verb = action.lookup("verb");
|
|
174
|
+
if ((verb == "start" || verb == "stop" || verb == "reset-failed") && subject.user == "jupyter") {
|
|
175
|
+
return polkit.Result.YES;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
If you have a sufficiently new polkit (124 or newer) you could also add
|
|
183
|
+
additional checks using `subject.system_unit == "jupyterhub.service"` to check
|
|
184
|
+
whether the this is being called from the context of `jupyterhub.service` and
|
|
185
|
+
`subject.no_new_privileges == true` that the `NoNewPrivileges=` setting on the
|
|
186
|
+
Hub is set.
|
|
187
|
+
|
|
188
|
+
### Unit files
|
|
189
|
+
|
|
190
|
+
This package ships with a script `jupyter-staticsystemdspawner-unitgenerator`
|
|
191
|
+
that can be used to generate unit files for users. These are meant as examples
|
|
192
|
+
and should be changed to fit the needs of your deployment.
|
|
193
|
+
|
|
194
|
+
They use a wrapper script (`jupyter-singleuser-wrapper`) for environment setup,
|
|
195
|
+
since JupyterHub usually reads its configuration from environment files. Since
|
|
196
|
+
the hub is running as an unprivileged users and so are the singleuser servers,
|
|
197
|
+
systemd credentials are used to pass information from the hub to the singleuser
|
|
198
|
+
servers. This wrapper script will therefore be needed to start the singleuser
|
|
199
|
+
servers.
|
|
200
|
+
|
|
201
|
+
An example for such a file:
|
|
202
|
+
```ini
|
|
203
|
+
[Unit]
|
|
204
|
+
Description=Jupyterhub Single-User Server for user foobar
|
|
205
|
+
|
|
206
|
+
[Service]
|
|
207
|
+
Type=simple
|
|
208
|
+
User=foobar
|
|
209
|
+
|
|
210
|
+
Environment="PATH=/opt/jupyter/hubenv/bin:/usr/bin:/usr/local/bin"
|
|
211
|
+
|
|
212
|
+
LoadCredential=spawnerconf:/run/jupyterhub/spawnerconf/foobar/default
|
|
213
|
+
ExecStart=/opt/jupyter/hubenv/bin/jupyter-singleuser-wrapper
|
|
214
|
+
|
|
215
|
+
WorkingDirectory=/home/foobar
|
|
216
|
+
StateDirectory=jupyterhub-foobar
|
|
217
|
+
|
|
218
|
+
# ensure that one singleuser server going over the memory limit
|
|
219
|
+
# does not kill all the user's servers
|
|
220
|
+
OOMPolicy=continue
|
|
221
|
+
# use the jupyterhub PAM module for session setup (see below)
|
|
222
|
+
PAMName=jupyterhub
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
If you want to configure settings for all singleuser servers, it is recommend to use drop in configuration and make use of the dash namespacing of units, e.g.
|
|
226
|
+
```ini
|
|
227
|
+
# jupyterhub-singleuser-.service.d/30-hardening.conf
|
|
228
|
+
[Service]
|
|
229
|
+
PrivateTmp=yes
|
|
230
|
+
PrivateDevices=yes
|
|
231
|
+
ProtectSystem=strict
|
|
232
|
+
ProtectKernelTunables=yes
|
|
233
|
+
ProtectControlGroups=yes
|
|
234
|
+
ReadWritePaths=/home/%j
|
|
235
|
+
NoNewPrivileges=yes
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
If you want to configure resource constraints, it is recommend to do that via a
|
|
239
|
+
slice unit and configuring `Slice=` for the singleuser unit. For an alternative
|
|
240
|
+
approach using PAM see below.
|
|
241
|
+
|
|
242
|
+
### PAM config
|
|
243
|
+
|
|
244
|
+
When using the `PAMName` service setting an existing PAM module needs to be
|
|
245
|
+
provided that will do session setup. This setting is independent of the used
|
|
246
|
+
authenticator.
|
|
247
|
+
|
|
248
|
+
Using a PAM module one can register JupyterHub singleuser sessions with `loginctl`.
|
|
249
|
+
|
|
250
|
+
The following example is adapted from the `login` PAM module.
|
|
251
|
+
```
|
|
252
|
+
#
|
|
253
|
+
# The PAM configuration file for the JupyterHub systemd-spawner service
|
|
254
|
+
#
|
|
255
|
+
# SELinux needs to be the first session rule. This ensures that any
|
|
256
|
+
# lingering context has been cleared. Without this it is possible
|
|
257
|
+
# that a module could execute code in the wrong domain.
|
|
258
|
+
# When the module is present, "required" would be sufficient (When SELinux
|
|
259
|
+
# is disabled, this returns success.)
|
|
260
|
+
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
|
|
261
|
+
|
|
262
|
+
# Sets the loginuid process attribute
|
|
263
|
+
session required pam_loginuid.so
|
|
264
|
+
|
|
265
|
+
# SELinux needs to intervene at login time to ensure that the process
|
|
266
|
+
# starts in the proper default security context. Only sessions which are
|
|
267
|
+
# intended to run in the user's context should be run after this.
|
|
268
|
+
# pam_selinux.so changes the SELinux context of the used TTY and configures
|
|
269
|
+
# SELinux in order to transition to the user context with the next execve()
|
|
270
|
+
# call.
|
|
271
|
+
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
|
|
272
|
+
# When the module is present, "required" would be sufficient (When SELinux
|
|
273
|
+
# is disabled, this returns success.)
|
|
274
|
+
|
|
275
|
+
# This module parses environment configuration file(s)
|
|
276
|
+
# and also allows you to use an extended config
|
|
277
|
+
# file /etc/security/pam_env.conf.
|
|
278
|
+
#
|
|
279
|
+
# parsing /etc/environment needs "readenv=1"
|
|
280
|
+
session required pam_env.so readenv=1
|
|
281
|
+
# locale variables are also kept into /etc/default/locale in etch
|
|
282
|
+
# reading this file *in addition to /etc/environment* does not hurt
|
|
283
|
+
session required pam_env.so readenv=1 envfile=/etc/default/locale
|
|
284
|
+
|
|
285
|
+
# Sets up user limits according to /etc/security/limits.conf
|
|
286
|
+
# (Replaces the use of /etc/limits in old login)
|
|
287
|
+
session required pam_limits.so
|
|
288
|
+
|
|
289
|
+
# Prints the last login info upon successful login
|
|
290
|
+
# (Replaces the `LASTLOG_ENAB' option from login.defs)
|
|
291
|
+
session optional pam_lastlog.so
|
|
292
|
+
|
|
293
|
+
# Create a new session keyring.
|
|
294
|
+
session optional pam_keyinit.so force revoke
|
|
295
|
+
|
|
296
|
+
# Standard Un*x account and session
|
|
297
|
+
@include common-session
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
When using such a setup the resource constraints for a user can be configured on
|
|
301
|
+
their user slice, e.g.
|
|
302
|
+
```ini
|
|
303
|
+
# user-123456.slice
|
|
304
|
+
[Slice]
|
|
305
|
+
TasksMax=33%
|
|
306
|
+
CPUQuota=400%
|
|
307
|
+
MemoryHigh=16G
|
|
308
|
+
MemoryMax=17G
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Configuration
|
|
312
|
+
|
|
313
|
+
For the most part configuration besides setting the spawner class should not be
|
|
314
|
+
required.
|
|
315
|
+
|
|
316
|
+
### `default_server_unit_name_template`
|
|
317
|
+
|
|
318
|
+
The name for the unit that is started by the spawner. It defaults to
|
|
319
|
+
`jupyterhub-singleuser-{USERNAME}.service` and the username is interpolated by
|
|
320
|
+
the spawner. Since the user is identified by the last part of the name after the
|
|
321
|
+
last dash and before the `.service` extension, names not following this format
|
|
322
|
+
will probably not work as exptected.
|
|
323
|
+
|
|
324
|
+
### `named_server_unit_name_template`
|
|
325
|
+
|
|
326
|
+
The name for the unit that is started by the spawner. It defaults to
|
|
327
|
+
`jupyterhub-singleuser-{USERNAME}@.service` and the username is interpolated by
|
|
328
|
+
the spawner. Since the user is identified by the last part of the name after the
|
|
329
|
+
last dash and before the @, names not following this format will probably not
|
|
330
|
+
work as exptected.
|
|
331
|
+
|
|
332
|
+
The unit instance passed in will be the name of the named server escaped with
|
|
333
|
+
`systemd-escape`, i.e. to get the unescaped name you will have to use the `%I`
|
|
334
|
+
specifier in the unit file.
|
|
335
|
+
|
|
336
|
+
### `unit_generator`
|
|
337
|
+
|
|
338
|
+
The name of a template unit started when a user unit does not exist. It can be
|
|
339
|
+
used to generate them. Defaults to `None`, which means unit files will have to
|
|
340
|
+
be generated for all users beforehand.
|
|
341
|
+
|
|
342
|
+
## Getting help
|
|
343
|
+
|
|
344
|
+
We encourage you to ask questions in the repository.
|
|
345
|
+
|
|
346
|
+
## License
|
|
347
|
+
|
|
348
|
+
We use a shared copyright model that enables all contributors to maintain the
|
|
349
|
+
copyright on their contributions.
|
|
350
|
+
|
|
351
|
+
All code is licensed under the terms of the revised BSD license.
|
|
352
|
+
|
|
353
|
+
## Resources
|
|
354
|
+
|
|
355
|
+
### JupyterHub and systemdspawner
|
|
356
|
+
|
|
357
|
+
- [Documentation for JupyterHub](http://jupyterhub.readthedocs.io/en/latest/) | [PDF (latest)](https://media.readthedocs.org/pdf/jupyterhub/latest/jupyterhub.pdf) | [PDF (stable)](https://media.readthedocs.org/pdf/jupyterhub/stable/jupyterhub.pdf)
|
|
358
|
+
- [Documentation for JupyterHub's REST API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/jupyterhub/master/docs/rest-api.yml#/default)
|
|
359
|
+
|
|
360
|
+
### Jupyter
|
|
361
|
+
|
|
362
|
+
- [Documentation for Project Jupyter](http://jupyter.readthedocs.io/en/latest/index.html) | [PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)
|
|
363
|
+
- [Project Jupyter website](https://jupyter.org)
|