From 8483b5890592dd19928feb5911b519f977932374 Mon Sep 17 00:00:00 2001 From: jvino Date: Wed, 29 Nov 2023 09:28:22 +0100 Subject: [PATCH] Cloud 1996 (#30) * Fix documentation, add variable checks, re-organise IAM-related tasks --------- Co-authored-by: gioacchino.vino --- README.md | 11 +++-- defaults/main.yml | 2 + tasks/iam-client.yml | 64 ++++++++++++++++++++++++++++ tasks/prepare-jupyter-hub.yml | 79 +++++++---------------------------- 4 files changed, 89 insertions(+), 67 deletions(-) create mode 100644 tasks/iam-client.yml diff --git a/README.md b/README.md index c7bb4c7..672ee8a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ A role to setup a node with a jupyterhub that spawns jupyterlab with or without Requirements ------------ -There are no requirements. This role will setup all the necessary dependencies. +This role needs a working IAM Client in order to configure the JupyterHub authentication plugin. +If you are using this role as it is and not through [INFN PaaS orchestrator](https://my.cloud.infn.it), you should provide `iam_url`, `iam_client_id` and `iam_token` variables. Role Variables -------------- @@ -34,10 +35,12 @@ Variable for the monitoring service: - `jupyterlab_collaborative`: bool, if to deploy the collaborative service (default: `no`) - `jupyterlab_collaborative_use_gpu`: bool, if to enable the collaborative service to use the GPU (default: `no`) - `jupyterlab_collaborative_image`: string, the collaborative Docker image (default: `"dodasts/snj-base-jlabc:v1.1.1-snj"`) -- `iam_url`: URL of the IAM service +- `iam_url`: URL of the IAM service (`Mandatory`) - `iam_groups`: string with the name of the IAM groups allowed (space separated) - `iam_admin_groups`: string with the name of the IAM groups that will be admin (space separated) -- `iam_subject` : token subject of the user deploying the service +- `iam_subject` : string, token subject of the user deploying the service +- `iam_client_id`: string, IAM client id (`Mandatory`) +- `iam_token` : string, token needed to interact with the IAM Issuer (`Mandatory`) - `server_ip`: string with the ip of the current server - `monitoring`: bool, if to deploy the Grafana monitoring service (default: `yes`) - `grafana_port`: int, the grafana service port @@ -86,6 +89,8 @@ Including an example of how to use your role (for instance, with variables passe run_jupyter: no iam_url: https://iam.example.service.it iam_groups: groupA + iam_client_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + iam_token: ``` diff --git a/defaults/main.yml b/defaults/main.yml index 2f5467e..b622d0f 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -41,6 +41,8 @@ iam_admin_groups: "" # group1 group2 server_ip: "" # 192.168.1.42 dns_name: "" iam_subject: "" +iam_client_id: "" +iam_token: "" # monitoring monitoring: yes diff --git a/tasks/iam-client.yml b/tasks/iam-client.yml new file mode 100644 index 0000000..ddbbd24 --- /dev/null +++ b/tasks/iam-client.yml @@ -0,0 +1,64 @@ +--- +- name: Retrieve registration endpoint from OpenID configuration + uri: + url: "{{ iam_url }}/.well-known/openid-configuration" + method: GET + return_content: yes + register: openid_config +- name: Set registration endpoint variable + set_fact: + registration_endpoint: "{{ openid_config.json.registration_endpoint }}" + +- name: Retrieve the IAM client info + uri: + url: "{{ registration_endpoint }}/{{ iam_client_id }}" + method: GET + headers: + Authorization: "Bearer {{ iam_token }}" + status_code: 200 + return_content: yes + register: iam_client_get_response + +- name: Save the IAM Client info locally + ansible.builtin.copy: + content: "{{ iam_client_get_response.json | to_nice_json }}" + dest: /usr/local/share/dodasts/jupyterhub/cookies/.client-iam.json + +- name: Define the new redirect_uri variable + ansible.builtin.set_fact: + redirect_uri: "https://{{ dns_name }}:{{ jupyter_port }}/hub/oauth_callback" + +- name: Import the local IAM Client info file + ansible.builtin.set_fact: + iam_client_info: "{{ lookup('file', '/usr/local/share/dodasts/jupyterhub/cookies/.client-iam.json') }}" + +- name: Update the redirect_uris field + ansible.builtin.set_fact: + updated_iam_client_info: "{{ iam_client_info | combine({'redirect_uris': [redirect_uri]}) }}" + +- name: Save the updated IAM Client info locally + ansible.builtin.copy: + content: "{{ updated_iam_client_info | to_json }}" + dest: /tmp/updated-iam-client.json + +- name: Import the local updated IAM Client info file + ansible.builtin.slurp: + src: /tmp/updated-iam-client.json + register: iam_client_file + +- name: Update the IAM client info remotely + uri: + url: "{{ registration_endpoint }}/{{ iam_client_id }}" + method: PUT + headers: + Authorization: "Bearer {{ iam_token }}" + Content-Type: application/json + body_format: json + body: "{{ iam_client_file.content | b64decode | from_json }}" + status_code: 200 + register: iam_response + +- name: Delete local IAM Client info file + ansible.builtin.file: + path: /tmp/updated-iam-client.json + state: absent diff --git a/tasks/prepare-jupyter-hub.yml b/tasks/prepare-jupyter-hub.yml index 48e846a..d710e52 100644 --- a/tasks/prepare-jupyter-hub.yml +++ b/tasks/prepare-jupyter-hub.yml @@ -167,70 +167,19 @@ cacheable: yes when: (jupyter_use_gpu | bool) -- block: - - name: Retrieve registration endpoint from OpenID configuration - uri: - url: "{{ iam_url }}/.well-known/openid-configuration" - method: GET - return_content: yes - register: openid_config - - name: Set registration endpoint variable - set_fact: - registration_endpoint: "{{ openid_config.json.registration_endpoint }}" - - - name: Collect the IAM client info - uri: - url: "{{ registration_endpoint }}/{{ iam_client_id }}" - method: GET - headers: - Authorization: "Bearer {{ iam_token }}" - status_code: 200 - return_content: yes - register: iam_client_get_response - - - name: Save the IAM Client info locally - ansible.builtin.copy: - content: "{{ iam_client_get_response.json | to_nice_json }}" - dest: /usr/local/share/dodasts/jupyterhub/cookies/.client-iam.json - - - name: Define the new redirect_uri variable - ansible.builtin.set_fact: - redirect_uri: "https://{{ dns_name }}:{{ jupyter_port }}/hub/oauth_callback" - - - name: Import the local IAM Client info file - ansible.builtin.set_fact: - iam_client_info: "{{ lookup('file', '/usr/local/share/dodasts/jupyterhub/cookies/.client-iam.json') }}" - - - name: Update the redirect_uris field - ansible.builtin.set_fact: - updated_iam_client_info: "{{ iam_client_info | combine({'redirect_uris': [redirect_uri]}) }}" - - - name: Save the updated IAM Client info locally - ansible.builtin.copy: - content: "{{ updated_iam_client_info | to_json }}" - dest: /tmp/updated-iam-client.json - - - name: Import the local updated IAM Client info file - ansible.builtin.slurp: - src: /tmp/updated-iam-client.json - register: iam_client_file - - - name: Update the IAM client info remotely - uri: - url: "{{ registration_endpoint }}/{{ iam_client_id }}" - method: PUT - headers: - Authorization: "Bearer {{ iam_token }}" - Content-Type: application/json - body_format: json - body: "{{ iam_client_file.content | b64decode | from_json }}" - status_code: 200 - register: iam_response - - - name: Delete local IAM Client info file - ansible.builtin.file: - path: /tmp/updated-iam-client.json - state: absent +# ---------- IAM Client retrieving, updating and local saving ---------- +- name: Check vars before interacting with the IAM issuer + assert: + that: + - iam_url | length > 0 + - iam_client_id | length > 0 + - iam_token | length > 0 + - dns_name | length > 0 + - jupyter_port | length > 0 + fail_msg: Not defined variable among iam_url, iam_client_id, iam_token, dns_name and jupyter_port. + +- name: Collect, Update and store locally the IAM Client info + include_tasks: iam-client.yml # ---------- Collect MIG GPU UUIDs from nvidia-smi ---------- - name: Collect MIG GPU UUIDs @@ -260,6 +209,7 @@ iam_client_id: "{{ iam_response.json.client_id }}" iam_client_secret: "{{ iam_response.json.client_secret }}" when: cert_manager_type != "self-signed" + - name: prepare compose file (private network) template: src: jupyter_hub_priv-compose.j2 @@ -275,3 +225,4 @@ with_items: "{{ jupyter_images.split() + [ jupyterlab_collaborative_image, compose_base_jhub_image, compose_base_http_proxy_image, compose_base_collab_http_proxy_image ] }}" async: 1800 # 30 min poll: 0 +