diff --git a/hosts/tsuki/services/jupyter.nix b/hosts/tsuki/services/jupyter.nix index 6abf2f6..573ed38 100644 --- a/hosts/tsuki/services/jupyter.nix +++ b/hosts/tsuki/services/jupyter.nix @@ -1,15 +1,23 @@ -{ pkgs, secrets, ... }: -{ - users.users.jupyter = { - group = "jupyter"; +{ config, pkgs, lib, ... }: let + cfg = config.services.jupyter; +in { + sops.secrets."jupyter/password" = { + restartUnits = [ "jupyter.service" ]; + owner = cfg.user; + inherit (cfg) group; }; + users.users."jupyter".group = "jupyter"; + users.groups."jupyter".members = [ "nginx" ]; + services.jupyter = { enable = true; group = "jupyter"; - ip = "0.0.0.0"; - port = secrets.ports.jupyterhub; - password = secrets.keys.hashed.jupyter; + password = let + readFile = f: "open('${f}', 'r', encoding='utf8').read().strip()"; + in + readFile config.sops.secrets."jupyter/password".path; + kernels = { pythonDS = let env = (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ @@ -32,4 +40,59 @@ }; }; }; + + systemd.services.jupyter = let + notebookConfig = pkgs.writeText "jupyter_config.py" '' + c.NotebookApp.notebook_dir = 'notebooks' + c.NotebookApp.open_browser = False + c.NotebookApp.password = ${cfg.password} + c.NotebookApp.password_required = True + + c.NotebookApp.sock = '/run/jupyter/jupyter.sock' + c.NotebookApp.sock_mode = '0660' + c.NotebookApp.local_hostnames = ['py.nani.wtf'] + + c.ConnectionFileMixin.transport = 'ipc' + + ${cfg.notebookConfig} + ''; + in { + environment = { + JUPYTER_DATA_DIR = "$STATE_DIRECTORY/data"; + JUPYTER_RUNTIME_DIR = "$RUNTIME_DIRECTORY"; + }; + serviceConfig = { + RuntimeDirectory = "jupyter"; + StateDirectory = "jupyter"; + + # Hardening + CapabilityBoundingSet = ""; + LockPersonality = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictSUIDSGID = true; + UMask = "0007"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + SystemCallArchitectures = "native"; + + ExecStartPre = '' + ${pkgs.coreutils}/bin/mkdir -p /var/lib/jupyter/{notebooks,data} + ''; + ExecStart = lib.mkForce '' + ${cfg.package}/bin/${cfg.command} --NotebookApp.config_file=${notebookConfig} + ''; + }; + }; } diff --git a/hosts/tsuki/services/nginx/default.nix b/hosts/tsuki/services/nginx/default.nix index 98abcba..24a2ee9 100644 --- a/hosts/tsuki/services/nginx/default.nix +++ b/hosts/tsuki/services/nginx/default.nix @@ -44,7 +44,7 @@ "hydra".servers."localhost:${s config.services.hydra.port}" = { }; "idrac".servers."${ips.idrac}" = { }; "invidious".servers."localhost:${s config.services.invidious.port}" = { }; - "jupyter".servers."localhost:${s ports.jupyterhub}" = { }; + "jupyter".servers."unix:/run/jupyter/jupyter.sock" = { }; "kanidm".servers."localhost:8300" = { }; "osuchan".servers."localhost:${s ports.osuchan}" = { }; "pgadmin".servers."unix:${config.services.uwsgi.instance.vassals.pgadmin.socket}" = { }; diff --git a/secrets/default.yaml b/secrets/default.yaml index dc65344..9d1a1d5 100644 --- a/secrets/default.yaml +++ b/secrets/default.yaml @@ -1,3 +1,5 @@ +jupyter: + password: ENC[AES256_GCM,data:mm0EHzhK9AqErfsoWWJ5+3ym+VXgEcZ+qadTy3f+NtA=,iv:ntGxklA5oDbGbo3j3ffbAvzGE4c9Ay/SfCWdA6bqzP4=,tag:KG1luMcSjBFm0LVKnoTvGA==,type:str] grafana: oauth2_secret: ENC[AES256_GCM,data:zxfPtiB/o5cC27O9uQzPvQV1qWcp3xxnIi7/P84I2lJ/X4ovAwXuiEqnc7BDAE4E,iv:ZY8BDTMEvR2JiFHKM8iM90UQbmTqH/DoVklWno6Xa4U=,tag:E8GTGk9IJauCgjaoToShBg==,type:str] secretkey: ENC[AES256_GCM,data:aVzqZqwFfm3FcYJE8USxsDbZVwtnF5NJXTAqshv9av4ZeR5YrDfDzLYHHztXMZt2Q7p/6A==,iv:A7x7oRUVvfxqSXRfi9+15z9pE6xX+GZrGU7gXrSKyXE=,tag:2uatRT0XePk2dqZj2ZlM3A==,type:str]