zero-to-jupyterhub-k8s
zero-to-jupyterhub-k8s copied to clipboard
Open forum about user server pre-startup security patch script
trafficstars
This issue is linked from https://github.com/jupyterhub/jupyter-server-proxy/security/advisories/GHSA-w3vc-fx9p-wp4v from within the configuration below, meant to help patch the vulnerability within a user server before the jupyter server starts.
This issue is meant to enable anyone with an interest to say something related to it say it in a context where others interested in this can read it.
singleuser:
cmd:
- /mnt/ghsa-w3vc-fx9p-wp4v/check-patch-run
- jupyterhub-singleuser
extraFiles:
ghsa-w3vc-fx9p-wp4v-check-patch-run:
mountPath: /mnt/ghsa-w3vc-fx9p-wp4v/check-patch-run
mode: 0755
stringData: |
#!/usr/bin/env python3
"""
This script is designed to check for and conditionally patch GHSA-w3vc-fx9p-wp4v
in user servers started by a JupyterHub. The script will execute any command
passed via arguments if provided, allowing it to wrap a user server startup call
to `jupyterhub-singleuser` for example.
Use and function of this script can be further discussed in
https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/3360.
Script adjustments:
- UPGRADE_IF_VULNERABLE
- ERROR_IF_VULNERABLE
Script patching assumptions:
- script is run before the jupyter server starts
- pip is available
- pip has sufficient filesystem permissions to upgrade jupyter-server-proxy
Read more at https://github.com/jupyterhub/jupyter-server-proxy/security/advisories/GHSA-w3vc-fx9p-wp4v.
"""
import os
import subprocess
import sys
# adjust these to meet vulnerability mitigation needs
UPGRADE_IF_VULNERABLE = True
ERROR_IF_VULNERABLE = True
def check_vuln():
"""
Checks for the vulnerability by looking to see if __version__ is available
as it coincides with the patched versions (3.2.3 and 4.1.1).
"""
try:
import jupyter_server_proxy
return False if hasattr(jupyter_server_proxy, "__version__") else True
except:
return False
def get_version_specifier():
"""
Returns a pip version specifier for use with `--no-deps` meant to do as
little as possible besides patching the vulnerability and remaining
functional.
"""
old = ["jupyter-server-proxy>=3.2.3,<4"]
new = ["jupyter-server-proxy>=4.1.1,<5", "simpervisor>=1,<2"]
try:
if sys.version_info < (3, 8):
return old
from importlib.metadata import version
jsp_version = version("jupyter-server-proxy")
if int(jsp_version.split(".")[0]) < 4:
return old
except:
pass
return new
def patch_vuln():
"""
Attempts to patch the vulnerability by upgrading jupyter-server-proxy using
pip. Returns True if the patch is applied successfully, otherwise False.
"""
# attempt upgrade via pip, takes ~4 seconds
proc = subprocess.run(
[sys.executable, "-m", "pip", "--version"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
pip_available = proc.returncode == 0
if pip_available:
proc = subprocess.run(
[sys.executable, "-m", "pip", "install", "--no-deps"]
+ get_version_specifier()
)
if proc.returncode == 0:
return True
return False
def main():
if check_vuln():
warning_or_error = (
"ERROR" if ERROR_IF_VULNERABLE and not UPGRADE_IF_VULNERABLE else "WARNING"
)
print(
f"{warning_or_error}: jupyter-server-proxy __is vulnerable__ to GHSA-w3vc-fx9p-wp4v, see "
"https://github.com/jupyterhub/jupyter-server-proxy/security/advisories/GHSA-w3vc-fx9p-wp4v.",
flush=True,
)
if warning_or_error == "ERROR":
sys.exit(1)
if UPGRADE_IF_VULNERABLE:
print(
"INFO: Attempting to upgrade jupyter-server-proxy using pip...",
flush=True,
)
if patch_vuln():
print(
"INFO: Attempt to upgrade jupyter-server-proxy succeeded!",
flush=True,
)
else:
warning_or_error = "ERROR" if ERROR_IF_VULNERABLE else "WARNING"
print(
f"{warning_or_error}: Attempt to upgrade jupyter-server-proxy failed!",
flush=True,
)
if warning_or_error == "ERROR":
sys.exit(1)
if len(sys.argv) >= 2:
print("INFO: Executing provided command", flush=True)
os.execvp(sys.argv[1], sys.argv[1:])
else:
print("INFO: No command to execute provided", flush=True)
main()