From 926d3d29d098566ad7044f9b8457e48c2bfd00e7 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 10 Nov 2024 20:21:51 +1100 Subject: [PATCH 1/5] fix: enable docker for jupyterhub - install/manage docker --- hieradata/roles/apps/jupyter/hub.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hieradata/roles/apps/jupyter/hub.yaml b/hieradata/roles/apps/jupyter/hub.yaml index 24c171e..3e7eb51 100644 --- a/hieradata/roles/apps/jupyter/hub.yaml +++ b/hieradata/roles/apps/jupyter/hub.yaml @@ -4,8 +4,13 @@ profiles::packages::include: python3.12-pip: {} hiera_include: + - docker - profiles::nginx::simpleproxy +# manage docker +docker::version: latest +docker::curl_ensure: false + # manage a simple nginx reverse proxy profiles::nginx::simpleproxy::nginx_vhost: 'jupyterhub.query.consul' profiles::nginx::simpleproxy::nginx_aliases: From 61f5f1ce1fc6ca5f553120bcb247b8210653f238 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 10 Nov 2024 20:26:18 +1100 Subject: [PATCH 2/5] feat: add docker settings - list docker network and image - fix ldap_admin setting to be a list of users --- site/profiles/manifests/jupyter/jupyterhub.pp | 4 +++- site/profiles/templates/jupyterhub/config.py.erb | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/site/profiles/manifests/jupyter/jupyterhub.pp b/site/profiles/manifests/jupyter/jupyterhub.pp index af3bb7f..b551d27 100644 --- a/site/profiles/manifests/jupyter/jupyterhub.pp +++ b/site/profiles/manifests/jupyter/jupyterhub.pp @@ -17,7 +17,9 @@ class profiles::jupyter::jupyterhub ( String $ldap_bind_dn_template = 'cn={username},ou=people,ou=users,dc=main,dc=unkin,dc=net', Boolean $ldap_use_ssl = false, Array $ldap_allowed_groups = ['ou=jupyterhub_user,ou=groups,dc=main,dc=unkin,dc=net'], - Array $ldap_admin_groups = ['ou=jupyterhub_admin,ou=groups,dc=main,dc=unkin,dc=net'], + Array $ldap_admin_users = [], + String $docker_image = 'git.query.consul/unkin/almalinux8-jupyterinstance:latest', + String $docker_network = 'jupyterhub', ){ # ensure nodejs:20 is installed diff --git a/site/profiles/templates/jupyterhub/config.py.erb b/site/profiles/templates/jupyterhub/config.py.erb index d4f964b..acda6e5 100644 --- a/site/profiles/templates/jupyterhub/config.py.erb +++ b/site/profiles/templates/jupyterhub/config.py.erb @@ -39,5 +39,5 @@ c.LDAPAuthenticator.use_ssl = <%= @ldap_use_ssl ? 'True' : 'False' %> # Restrict access to a specific LDAP group c.LDAPAuthenticator.allowed_groups = <%= @ldap_allowed_groups.to_s %> -# Set an LDAP group as admins -c.LDAPAuthenticator.admin_groups = <%= @ldap_admin_groups.to_s %> +# List LDAP users as admins +c.LDAPAuthenticator.admin_users = <%= @ldap_admin_users.to_s %> From 42ad972697df3911fb934d8e97fe4c40cae3567e Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 10 Nov 2024 20:28:10 +1100 Subject: [PATCH 3/5] feat: add ldap configuration - add group members to jupyterhub_user - add svc_jupyterhub user for ldap binding - paramatarise all ldap fields required - manage the notebook data directory --- hieradata/roles/apps/jupyter/hub.eyaml | 1 + hieradata/roles/infra/auth/glauth.yaml | 9 +++++- site/profiles/manifests/jupyter/jupyterhub.pp | 29 ++++++++++--------- .../templates/jupyterhub/config.py.erb | 18 ++++++++---- 4 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 hieradata/roles/apps/jupyter/hub.eyaml diff --git a/hieradata/roles/apps/jupyter/hub.eyaml b/hieradata/roles/apps/jupyter/hub.eyaml new file mode 100644 index 0000000..6b5a91a --- /dev/null +++ b/hieradata/roles/apps/jupyter/hub.eyaml @@ -0,0 +1 @@ +profiles::jupyter::jupyterhub::ldap_bind_pass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAJspN3e2WzA0uZaLgFZ0Ewqii9dY0tTgbirsW70M2VZtLY+s+C6HE8ZZUtpfnRsFwUUhOj7s25X9xVOZNTpZIGPyfx9MWlSyFw2RFuXSEwaydf1DcBbg8261YrTTysA4Jsa1L4DLsX55q+XJUyeUbimVQkIacVIvzTdnZCBKnVNUh3U2PNAmV7SOL2KH8Jpbfs/EQfBt8XuGMCg3I/4RDyoNERqthW6W2KiMX2Gmd8iQ5+W9udH0lEAMx415oyImmN+dEuThcx9FGMi8BWYtnxH96yWafpT5qltwW6EVzIGWuLhiD1LcWYc5RB8jc3DhbeouChpKsN6c4EHoKt3aWsTBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBC8jcnqilJgY1/AnHWHfX4bgDCi2a3Rj43Z0dgfB5HaHdpfked3Cx+u94r2S5+Cg3QogU1AIF04rjzOL+bD2HdaMfo=] diff --git a/hieradata/roles/infra/auth/glauth.yaml b/hieradata/roles/infra/auth/glauth.yaml index 3348ffb..9e77ecf 100644 --- a/hieradata/roles/infra/auth/glauth.yaml +++ b/hieradata/roles/infra/auth/glauth.yaml @@ -63,7 +63,8 @@ glauth::users: - 20018 - 20023 - 20024 - - 20025 + - 20025 # jupyterhub_admin + - 20026 # jupyterhub_user loginshell: '/bin/bash' homedir: '/home/benvin' passsha256: 'd2434f6b4764ef75d5b7b96a876a32deedbd6aa726a109c3f32e823ca66f604a' @@ -242,6 +243,12 @@ glauth::services: uidnumber: 30009 primarygroup: 20001 passsha256: 'd63b04884d5c7d630b0c06896046065a0926ac5c3d6177ef85320e5fa1be00b9' + svc_jupyterhub: + service_name: 'svc_jupyterhub' + mail: 'jupyterhub@service.main.unkin.net' + uidnumber: 30010 + primarygroup: 20001 + passsha256: '09db1e0c2498214da35f3f2ed46a90a7b90635c207f8725e7abf76b48345a39b' glauth::groups: users: diff --git a/site/profiles/manifests/jupyter/jupyterhub.pp b/site/profiles/manifests/jupyter/jupyterhub.pp index b551d27..405c1bd 100644 --- a/site/profiles/manifests/jupyter/jupyterhub.pp +++ b/site/profiles/manifests/jupyter/jupyterhub.pp @@ -3,6 +3,7 @@ class profiles::jupyter::jupyterhub ( Stdlib::AbsolutePath $base_path = '/opt/jupyterhub', Stdlib::AbsolutePath $venv_path = "${base_path}/venv", Stdlib::AbsolutePath $config_path = "${base_path}/config.py", + Stdlib::AbsolutePath $notebook_path = '/home/jupyter/work', Hash $vault_config = {}, String $owner = 'jupyterhub', String $group = 'jupyterhub', @@ -13,13 +14,19 @@ class profiles::jupyter::jupyterhub ( 'dockerspawner', 'jupyterhub-ldapauthenticator', ], - String $ldap_server_address = 'ldap://ldap.service.consul', - String $ldap_bind_dn_template = 'cn={username},ou=people,ou=users,dc=main,dc=unkin,dc=net', - Boolean $ldap_use_ssl = false, - Array $ldap_allowed_groups = ['ou=jupyterhub_user,ou=groups,dc=main,dc=unkin,dc=net'], - Array $ldap_admin_users = [], - String $docker_image = 'git.query.consul/unkin/almalinux8-jupyterinstance:latest', - String $docker_network = 'jupyterhub', + String $ldap_server_address = 'ldap://ldap.service.consul', + String $ldap_tls_strategy = 'insecure', + Array $ldap_allowed_groups = ['ou=jupyterhub_user,ou=groups,dc=main,dc=unkin,dc=net'], + Array $ldap_admin_users = [], + String $ldap_bind_user = 'cn=svc_jupyterhub,ou=services,ou=users,dc=main,dc=unkin,dc=net', + String $ldap_bind_pass = 'change-me', + String $ldap_user_search_base = 'ou=people,ou=users,dc=main,dc=unkin,dc=net', + String $ldap_user_search_filter = '({login_attr}={login})', + String $ldap_group_search_filter = '(uniqueMember={userdn})', + String $ldap_user_attribute = 'uid', + String $ldap_user_dn_attribute = 'cn', + String $docker_image = 'git.query.consul/unkin/almalinux8-jupyterinstance:latest', + String $docker_network = 'bridge', ){ # ensure nodejs:20 is installed @@ -93,7 +100,7 @@ class profiles::jupyter::jupyterhub ( username => $owner, uid => 1101, gid => 1101, - groups => ['systemd-journal'], + groups => ['systemd-journal', 'docker'], system => true, } @@ -107,11 +114,5 @@ class profiles::jupyter::jupyterhub ( ], } - ## create symbolic link in $PATH - #file { "/usr/local/bin/${script_name}": - # ensure => 'link', - # target => "${base_path}/${script_name}", - # require => File["${base_path}/${script_name}"], - #} } } diff --git a/site/profiles/templates/jupyterhub/config.py.erb b/site/profiles/templates/jupyterhub/config.py.erb index acda6e5..7aad2c6 100644 --- a/site/profiles/templates/jupyterhub/config.py.erb +++ b/site/profiles/templates/jupyterhub/config.py.erb @@ -16,12 +16,11 @@ c.DockerSpawner.image = '<%= @docker_image %>' c.DockerSpawner.network_name = '<%= @docker_network %>' # Notebook directory and mount location -notebook_dir = '/home/jupyter/work' -c.DockerSpawner.notebook_dir = notebook_dir +c.DockerSpawner.notebook_dir = '<%= @notebook_path %>' # Optional: Volume mapping for user data persistence c.DockerSpawner.volumes = { - 'jupyterhub-user-{username}': notebook_dir + 'jupyterhub-user-{username}': '<%= @notebook_path %>' } # DockerSpawner options @@ -33,11 +32,20 @@ c.JupyterHub.authenticator_class = 'ldapauthenticator.LDAPAuthenticator' # LDAP Server settings c.LDAPAuthenticator.server_address = '<%= @ldap_server_address %>' -c.LDAPAuthenticator.bind_dn_template = '<%= @ldap_bind_dn_template %>' -c.LDAPAuthenticator.use_ssl = <%= @ldap_use_ssl ? 'True' : 'False' %> +c.LDAPAuthenticator.tls_strategy = '<%= @ldap_tls_strategy %>' # Restrict access to a specific LDAP group c.LDAPAuthenticator.allowed_groups = <%= @ldap_allowed_groups.to_s %> # List LDAP users as admins c.LDAPAuthenticator.admin_users = <%= @ldap_admin_users.to_s %> + +# Lookup settings +c.LDAPAuthenticator.lookup_dn = True +c.LDAPAuthenticator.lookup_dn_search_filter = '<%= @ldap_user_search_filter %>' +c.LDAPAuthenticator.lookup_dn_search_user = '<%= @ldap_bind_user %>' +c.LDAPAuthenticator.lookup_dn_search_password = '<%= @ldap_bind_pass %>' +c.LDAPAuthenticator.user_search_base = '<%= @ldap_user_search_base %>' +c.LDAPAuthenticator.user_attribute = '<%= @ldap_user_attribute %>' +c.LDAPAuthenticator.lookup_dn_user_dn_attribute = '<%= @ldap_user_dn_attribute %>' +c.LDAPAuthenticator.group_search_filter = '<%= @ldap_group_search_filter %>' From 92a9655a5020b8c8895e2c8fd71ae73d1ea16c34 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sat, 16 Nov 2024 19:54:19 +1100 Subject: [PATCH 4/5] feat: jupyterhub updates - always pull containers when starting new instance - enable access to terminal --- site/profiles/templates/jupyterhub/config.py.erb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/profiles/templates/jupyterhub/config.py.erb b/site/profiles/templates/jupyterhub/config.py.erb index 7aad2c6..071c87f 100644 --- a/site/profiles/templates/jupyterhub/config.py.erb +++ b/site/profiles/templates/jupyterhub/config.py.erb @@ -9,6 +9,7 @@ c = get_config() c.JupyterHub.bind_url = 'http://:8000' c.JupyterHub.hub_ip = '0.0.0.0' c.JupyterHub.hub_port = 8081 +c.NotebookApp.enable_terminals = True # Configure the DockerSpawner c.JupyterHub.spawner_class = DockerSpawner @@ -26,6 +27,7 @@ c.DockerSpawner.volumes = { # DockerSpawner options c.DockerSpawner.remove = True c.DockerSpawner.debug = True +c.DockerSpawner.pull_policy = "always" # LDAP Authentication c.JupyterHub.authenticator_class = 'ldapauthenticator.LDAPAuthenticator' From 8586e9eb329ebf4272b08e30865b986ff6a01555 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sat, 16 Nov 2024 20:11:06 +1100 Subject: [PATCH 5/5] feat: enable web-sockets - change simpleproxy config for jupyter::hub role to use websockets --- hieradata/roles/apps/jupyter/hub.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hieradata/roles/apps/jupyter/hub.yaml b/hieradata/roles/apps/jupyter/hub.yaml index 3e7eb51..630fafe 100644 --- a/hieradata/roles/apps/jupyter/hub.yaml +++ b/hieradata/roles/apps/jupyter/hub.yaml @@ -18,10 +18,33 @@ profiles::nginx::simpleproxy::nginx_aliases: - jupyterhub.query.consul - "jupyterhub.service.%{facts.country}-%{facts.region}.consul" +profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_path: '/' +profiles::nginx::simpleproxy::use_default_location: false nginx::client_max_body_size: 20M +profiles::nginx::simpleproxy::locations: + # authorised access from external + default: + ensure: 'present' + server: "%{lookup('profiles::nginx::simpleproxy::nginx_vhost')}" + ssl_only: true + location: '/' + proxy: "http://%{lookup('profiles::nginx::simpleproxy::proxy_host')}:%{lookup('profiles::nginx::simpleproxy::proxy_port')}" + proxy_set_header: + - 'Host $host' + - 'X-Real-IP $remote_addr' + - 'X-Forwarded-For $proxy_add_x_forwarded_for' + - 'X-Forwarded-Host $host' + - 'X-Forwarded-Proto $scheme' + - 'Upgrade $http_upgrade' + - 'Connection $http_connection' + - 'X-Scheme $scheme' + proxy_redirect: 'off' + proxy_http_version: '1.1' + proxy_buffering: 'off' + # additional altnames profiles::pki::vault::alt_names: - jupyterhub.service.consul