Skip to content
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

Support native JupyterHub OAuthenticator in 2i2c-managed hubs #706

Merged
merged 22 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ff9cf07
Enable reading in extra, secret config when not using auth0 in deployer
sgibson91 Sep 23, 2021
3093a35
Enable additionalProperties for auth0 key in chart schema
sgibson91 Sep 23, 2021
d45b1d7
Add a helm chart schema for secrets/config/hubs
sgibson91 Sep 23, 2021
92887fc
Add some docs on setting up auth using GitHubOAuthenticator in Jupyte…
sgibson91 Sep 23, 2021
fe7cd40
Fix typo in docs
sgibson91 Sep 23, 2021
99e7f82
Add a `required: false` field for `auth0.connection`
sgibson91 Sep 23, 2021
f98a63c
Merge branch 'jupyterhub-oauth-github' of github.com:sgibson91/pilot-…
sgibson91 Sep 23, 2021
4f53a4c
Unconditionally read in secret config if it exists
sgibson91 Sep 23, 2021
ffb4b95
Unset hardcoded authenticator_class config, conditionally set it in d…
sgibson91 Sep 23, 2021
c2587f5
Update docs to reflect most recent fixes
sgibson91 Sep 23, 2021
7d1c6d7
Remove whitelist, update admonition on switching providers
sgibson91 Sep 23, 2021
abaa223
Add some more doc fixes
sgibson91 Sep 23, 2021
81c40be
Add links
sgibson91 Sep 27, 2021
93a792a
Update docs/howto/configure/auth-management.md
sgibson91 Sep 27, 2021
969869a
Add example of /hub/oauth_callback url
sgibson91 Sep 27, 2021
e3f88f0
Switch admonition blocks for notes
sgibson91 Sep 27, 2021
2646ecf
Update deployer/hub.py
sgibson91 Sep 27, 2021
ba6735e
Merge branch 'master' into jupyterhub-oauth-github
sgibson91 Sep 27, 2021
fe4ab01
Remove stray `users` key from code snippet in docs
sgibson91 Sep 28, 2021
c937694
Give more explicit fallback when reading secret config
sgibson91 Sep 29, 2021
5ca78c9
Clarify a note in docs
sgibson91 Sep 29, 2021
b1d6aac
Update dynamically set authenticator_class to be CustomOAuthenticator…
sgibson91 Sep 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/hubs/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ properties:
- basehub
- daskhub
auth0:
additionalProperties: false
additionalProperties: true
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
type: object
description: |
All hubs use Auth0 for authentication, and we dynamically fetch the credentials
Expand Down
17 changes: 16 additions & 1 deletion deployer/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,28 @@ def deploy(self, auth_provider, secret_key, skip_hub_health_test=False):
"""
Deploy this hub
"""
if self.spec["auth0"]['enabled'] == False:
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
# Auth0 has been disabled. Instead read in secret config.
secret_config_path = Path(os.getcwd()) / "secrets/config/hubs" / f'{self.cluster.spec["name"]}.cluster.yaml'
with decrypt_file(secret_config_path) as decrypted_file_path:
with open(decrypted_file_path) as f:
secret_config = yaml.load(f)

hubs = secret_config["hubs"]
secret_hub_config = next((hub for i, hub in enumerate(hubs) if hubs[i]["name"] == self.spec["name"]), None)
secret_hub_config = secret_hub_config["config"]
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
else:
secret_hub_config = {}

generated_values = self.get_generated_config(auth_provider, secret_key)

with tempfile.NamedTemporaryFile(mode='w') as values_file, tempfile.NamedTemporaryFile(mode='w') as generated_values_file:
with tempfile.NamedTemporaryFile(mode='w') as values_file, tempfile.NamedTemporaryFile(mode='w') as generated_values_file, tempfile.NamedTemporaryFile(mode='w') as secret_values_file:
json.dump(self.spec['config'], values_file)
json.dump(generated_values, generated_values_file)
json.dump(secret_hub_config, secret_values_file)
values_file.flush()
generated_values_file.flush()
secret_values_file.flush()

cmd = [
'helm', 'upgrade', '--install', '--create-namespace', '--wait',
Expand All @@ -457,6 +471,7 @@ def deploy(self, auth_provider, secret_key, skip_hub_health_test=False):
# we should put the config from config/hubs last.
'-f', generated_values_file.name,
'-f', values_file.name,
'-f', secret_values_file.name,
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
]

print(f"Running {' '.join(cmd)}")
Expand Down
109 changes: 107 additions & 2 deletions docs/howto/configure/auth-management.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Manage authentication

[auth0](https://auth0.com) provides authentication for all hubs here. It can
## Auth0

[auth0](https://auth0.com) provides authentication for the majority of 2i2c hubs. It can
be configured with many different [connections](https://auth0.com/docs/identityproviders)
that users can authenticate with - such as Google, GitHub, etc.

Expand Down Expand Up @@ -53,4 +55,107 @@ So we want to manage authentication by:

```{admonition} Switching auth
Switching authentication for a pre-existing hub will simply create new usernames. Any pre-existing users will no longer be able to access their accounts (although administrators will be able to do so). If you have pre-existing users and want to switch the hub authentication, rename the users to the new auth pattern (e.g. convert github handles to emails).
```
```

## Native JupyterHub OAuthenticator for GitHub Orgs and Teams

```{admonition}
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
This setup is currently only supported for communities that **require** authentication via a GitHub organisation or team.
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved

We may update this policy in the future.
```

For communities that require authenticating users against a GitHub organisation or team, we instead use the native JupyterHub OAuthenticator.
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
Presently, this involves a few more manual steps than the `auth0` setup described above.

1. **Create a GitHub OAuth App.**
This can be achieved by following [GitHub's documentation](https://docs.github.com/en/developers/apps/building-oauth-apps/creating-an-oauth-app).
- When naming the application, please follow the convention `<CLUSTER_NAME>-<HUB_NAME>` for consistency, e.g. `2i2c-staging` is the OAuth app for the staging hub running on the 2i2c cluster.
- The Homepage URL should match that in the `domain` field of the appropriate `*.cluster.yaml` file in the `pilot-hubs` repo.
- The authorisation callback URL is the homepage url appended with `/hub/oauth_callback`
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
- Once you have created the OAuth app, make a new of the client ID, generate a client secret and then hold on to these values for a future step

2. **Transfer the OAuth App to the `2i2c-org` GitHub account.**
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
By default, OAuth apps are created under your GitHub account.
However, it will be a point of failure if a hub's app is only accessible by a single member of the 2i2c Engineering team.
Therefore, please ensure you transfer it to the `2i2c-org` GitHub org by following [GitHub's documentation](https://docs.github.com/en/developers/apps/managing-oauth-apps/transferring-ownership-of-an-oauth-app).

3. **Create or update the appropriate secret config file under `secrets/config/hubs/*.cluster.yaml`.**
You should add the following config to this file, pasting in the client ID and secret you generated in step 1.

```yaml
hubs:
- name: HUB_NAME
config:
jupyterhub:
hub:
config:
GitHubOAuthenticator:
client_id: CLIENT_ID
client_secret: CLIENT_SECRET
```

```{note}
Add the `basehub` key between `config` and `jupyterhub` for `daskhub` deployments.
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
```

```{admonition}
Make sure this is encrypted with `sops` before committing it to the repository!
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved

`sops -i -e secrets/config/hub/*.cluster.yaml`
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
```

4. **Edit the non-secret config under `config/hubs`.**
You should make sure the matching hub config takes one of the following forms.

To authenticate against a GitHub organisation:
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved

```yaml
hubs:
- name: HUB_NAME
auth0:
enabled: false
connection: "" # This key is still required by the schema
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
... # Other config
config:
jupyterhub:
hub:
config:
JupyterHub:
authenticator_class: github
GitHubOAuthenticator:
oauth_callback_url: https://{{ HUB_DOMAIN }}/hub/oauth_callback
allowed_organizations:
- 2i2c-org
- ORG_NAME
scope:
- read:user
extraConfig:
06-custom-authenticator: "" # Required to override our overrides...
```

To authenticate against a GitHub Team:

```yaml
hubs:
- name: HUB_NAME
auth0:
enabled: false
connection: "" # This key is still required by the schema
... # Other config
config:
jupyterhub:
hub:
config:
JupyterHub:
authenticator_class: github
GitHubOAuthenticator:
oauth_callback_url: https://{{ HUB_DOMAIN }}/hub/oauth_callback
allowed_organizations:
- 2i2c-org:tech-team
- ORG_NAME:TEAM_NAME
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
scope:
- read:org
extraConfig:
06-custom-authenticator: "" # Required to override our overrides...
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
```
24 changes: 24 additions & 0 deletions secrets/config/hubs/schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
$schema: 'http://json-schema.org/draft-07/schema#'
sgibson91 marked this conversation as resolved.
Show resolved Hide resolved
type: object
additionalProperties: false
properties:
hubs:
type: array
description: |
Each item here is additional config for a hub deployed to this cluster.
required:
- name
- config
items:
- type: object
additionalProperties: false
properties:
name:
type: string
description: |
Name of the hub. This will be used to determine
the namespace the hub is deployed to
config:
type: object
description: |
YAML configuration containing secrets that is passed through to helm.