-
Notifications
You must be signed in to change notification settings - Fork 826
Seed secrets (proxy.secretToken, etc) so they don't have to be manually generated #1993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
698675e
8ce16b3
227d7fc
78b6d75
fc3f27e
4517d02
a2d0c23
b8c60ef
b94fec5
3c98c3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,54 +2,45 @@ | |
|
||
# Installing JupyterHub | ||
|
||
Now that we have a {doc}`Kubernetes cluster </kubernetes/setup-kubernetes>` and {doc}`Helm </kubernetes/setup-helm>` setup, we can proceed by using Helm to install JupyterHub | ||
and related {term}`Kubernetes resources <Kubernetes resource>` using a | ||
{term}`Helm chart`. | ||
|
||
## Prepare configuration file | ||
|
||
In this step we will prepare a [YAML](https://en.wikipedia.org/wiki/YAML) | ||
configuration file that we will refer to as `config.yaml`. It will contain the multiple | ||
{term}`Helm values` to be provided to a JupyterHub {term}`Helm chart` developed | ||
specifically together with this guide. | ||
|
||
Helm charts contains {term}`templates <Helm template>` that with provided values will render to {term}`Kubernetes resources <Kubernetes resource>` to be installed in a Kubernetes cluster. This | ||
config file will provide the values to be used by our Helm chart. | ||
|
||
1. Generate a random hex string representing 32 bytes to use as a security | ||
token. Run this command in a terminal and copy the output: | ||
|
||
```{code-block} bash | ||
|
||
openssl rand -hex 32 | ||
|
||
``` | ||
|
||
2. Create and start editing a file called `config.yaml`. In the code snippet | ||
below we start the widely available [nano editor](https://en.wikipedia.org/wiki/GNU_nano), but any editor will do. | ||
|
||
``` | ||
nano config.yaml | ||
``` | ||
|
||
3. Write the following into the `config.yaml` file but instead of writing | ||
`<RANDOM-HEX>` paste the generated hex string you copied in step 1. | ||
|
||
``` | ||
proxy: | ||
secretToken: "<RANDOM_HEX>" | ||
``` | ||
|
||
It is common practice for Helm and Kubernetes YAML files to indent using | ||
two spaces. | ||
|
||
4. Save the `config.yaml` file. In the nano editor this is done by pressing **CTRL+X** or | ||
**CMD+X** followed by a confirmation to save the changes. | ||
|
||
<!--- | ||
Don't put an example here! People will just copy paste that & that's a | ||
security issue. | ||
--> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There were significant differences here, so I ended up updating some leading paragraphs as well. |
||
With a {doc}`Kubernetes cluster </kubernetes/setup-kubernetes>` cluster | ||
available and {doc}`Helm </kubernetes/setup-helm>` installed, we can install | ||
JupyterHub in the Kubernetes cluster using the JupyterHub Helm chart. | ||
|
||
## Initialize a Helm chart configuration file | ||
|
||
Helm charts' contain {term}`templates <Helm template>` that can be rendered to | ||
the {term}`Kubernetes resources <Kubernetes resource>` to be installed. A user | ||
of a Helm chart can override the chart's default values to influence how the | ||
templates render. | ||
|
||
In this step we will initialize a chart configuration file for you to adjust | ||
your installation of JupyterHub. We will name and refer to it as `config.yaml` | ||
going onwards. | ||
|
||
```{admonition} Introduction to YAML | ||
If you haven't worked with YAML before, investing some | ||
minutes [learning about it]([YAML](https://www.youtube.com/watch?v=cdLNKUoMc6c) | ||
will likely be worth your time. | ||
``` | ||
|
||
As of version 1.0.0, you don't need any configuration to get started so you can | ||
just create a `config.yaml` file with some helpful comments. | ||
|
||
```yaml | ||
# This file can update the JupyterHub Helm chart's default configuration values. | ||
# | ||
# For reference see the configuration reference and default values, but make | ||
# sure to refer to the Helm chart version of interest to you! | ||
# | ||
# Introduction to YAML: https://www.youtube.com/watch?v=cdLNKUoMc6c | ||
# Chart config reference: https://zero-to-jupyterhub.readthedocs.io/en/stable/resources/reference.html | ||
# Chart default values: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/values.yaml | ||
# Available chart versions: https://jupyterhub.github.io/helm-chart/ | ||
# | ||
``` | ||
|
||
In case you are working from a terminal and are unsure how to create this file, | ||
can try with `nano config.yaml`. | ||
|
||
## Install JupyterHub | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,13 @@ | |
configuration_directory = os.path.dirname(os.path.realpath(__file__)) | ||
sys.path.insert(0, configuration_directory) | ||
|
||
from z2jh import get_config, set_config_if_not_none, get_name, get_name_env | ||
from z2jh import ( | ||
get_config, | ||
set_config_if_not_none, | ||
get_name, | ||
get_name_env, | ||
get_secret_value, | ||
) | ||
|
||
|
||
def camelCaseify(s): | ||
|
@@ -66,7 +72,6 @@ def camelCaseify(s): | |
("concurrent_spawn_limit", None), | ||
("active_server_limit", None), | ||
("base_url", None), | ||
# ('cookie_secret', None), # requires a Hex -> Byte transformation | ||
("allow_named_servers", None), | ||
("named_server_limit_per_user", None), | ||
("authenticate_prometheus", None), | ||
|
@@ -79,11 +84,6 @@ def camelCaseify(s): | |
cfg_key = camelCaseify(trait) | ||
set_config_if_not_none(c.JupyterHub, trait, "hub." + cfg_key) | ||
|
||
# a required Hex -> Byte transformation | ||
cookie_secret_hex = get_config("hub.cookieSecret") | ||
if cookie_secret_hex: | ||
c.JupyterHub.cookie_secret = a2b_hex(cookie_secret_hex) | ||
|
||
# hub_bind_url configures what the JupyterHub process within the hub pod's | ||
# container should listen to. | ||
hub_container_port = 8081 | ||
|
@@ -372,9 +372,19 @@ def camelCaseify(s): | |
c.Spawner.debug = True | ||
|
||
|
||
# load hub.config values | ||
# load potentially seeded secrets | ||
c.JupyterHub.proxy_auth_token = get_secret_value("JupyterHub.proxy_auth_token") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify: the change here is that these secrets are no longer optional in the chart, and cannot possibly be left unspecified or null, is that correct? The previous logic allowed these to be unspecified, in which case the existing default behavior would occur, but explicitly setting them to null or empty values is not the same thing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is correct that we will now always provide a value to these three configuration options, if you search for "assert hack" you will find an assertion I make about that. Any falsy value for proxy.secretToken, hub.cookieSecret, hub.config.CryptKeeper.keys would lead to automatic generation of a new passwords. Is this indirectly influencing the behavior of JupyterHub? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
c.JupyterHub.cookie_secret = a2b_hex(get_secret_value("JupyterHub.cookie_secret")) | ||
c.CryptKeeper.keys = get_secret_value("CryptKeeper.keys").split(";") | ||
|
||
# load hub.config values, except potentially seeded secrets | ||
for section, sub_cfg in get_config("hub.config", {}).items(): | ||
c[section].update(sub_cfg) | ||
if section == "JupyterHub" and sub_cfg in ["proxy_auth_token", "cookie_secret"]: | ||
pass | ||
elif section == "CryptKeeper" and sub_cfg in ["keys"]: | ||
pass | ||
else: | ||
c[section].update(sub_cfg) | ||
|
||
# execute hub.extraConfig string | ||
extra_config = get_config("hub.extraConfig", {}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an open issue to improve the debugging section, so instead of finding an example with diagnosis etc that wasn't correct any more, I opted to delete the section.