From 380430934fc05c0988a60b6d43d3e4810e7d2b97 Mon Sep 17 00:00:00 2001 From: chesterxgchen Date: Wed, 5 Feb 2025 20:31:21 -0800 Subject: [PATCH] site-specific security example mostly working except for keycloak one --- .../06.0_introduction/introduction.ipynb | 5 + .../Seurity_architecture.ipynb | 4 +- .../identity_security.ipynb | 2 +- .../README.md | 0 .../admin/local/custom/admin_auth.py | 0 .../admin/local/resources.json | 2 +- .../code/data/download.py | 0 .../code/fl_jobs.py | 53 ++ .../code/requirements.txt | 0 .../code/src/client.py | 0 .../code/src/fedavg.py | 0 .../code/src/network.py | 0 .../edit_site_local_resources.py | 65 +++ .../get_keycloak_access_token.py | 60 +++ .../keycloak-setup/docker-compose.yml | 24 +- .../keycloak-setup/dockerfile | 18 + .../keycloak-setup/init.sh | 7 +- .../keycloak_integration.ipynb | 362 ++++++++++++++ .../local/custom/keycloak_security_handler.py | 0 .../site/local/resources.json | 0 .../client_side_security_check.ipynb | 213 +++++++++ .../code/data/download.py | 60 +++ .../code/fl_jobs.py | 0 .../code/requirements.txt | 3 + .../code/src/client.py | 193 ++++++++ .../code/src/fedavg.py | 158 ++++++ .../code/src/network.py | 37 ++ .../site-1/custom/security_handler.py | 0 .../security/site-1/resources.json | 0 .../edit_site_local_resources.py | 64 +++ .../server/custom/security_handler.py | 0 .../server_side_security_plugin.ipynb | 163 +++++++ .../project.yml | 40 -- .../security/server/resources.json | 62 --- .../site_specific_security.ipynb | 451 +----------------- .../part-3_introduction.ipynb | 5 +- 36 files changed, 1490 insertions(+), 561 deletions(-) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/README.md (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/admin/local/custom/admin_auth.py (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/admin/local/resources.json (77%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples/custom_client_side_job_level_authorization => custom_client_side_auth_system_integration}/code/data/download.py (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/fl_jobs.py rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples/custom_client_side_job_level_authorization => custom_client_side_auth_system_integration}/code/requirements.txt (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples/custom_client_side_job_level_authorization => custom_client_side_auth_system_integration}/code/src/client.py (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples/custom_client_side_job_level_authorization => custom_client_side_auth_system_integration}/code/src/fedavg.py (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples/custom_client_side_job_level_authorization => custom_client_side_auth_system_integration}/code/src/network.py (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/edit_site_local_resources.py create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/get_keycloak_access_token.py rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml (62%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/dockerfile rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/keycloak-setup/init.sh (95%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak_integration.ipynb rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/site/local/custom/keycloak_security_handler.py (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_auth_system_integration/site/local/resources.json (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/client_side_security_check.ipynb create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/data/download.py rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_job_level_authorization/code/fl_jobs.py (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/requirements.txt create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/client.py create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/fedavg.py create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/network.py rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_job_level_authorization/security/site-1/custom/security_handler.py (100%) rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_client_side_job_level_authorization/security/site-1/resources.json (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/edit_site_local_resources.py rename examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/{examples => }/custom_server_side_authentication/security/server/custom/security_handler.py (100%) create mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/server_side_security_plugin.ipynb delete mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/project.yml delete mode 100644 examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/resources.json diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.0_introduction/introduction.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.0_introduction/introduction.ipynb index 4ccdd7c09d..b5c8683127 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.0_introduction/introduction.ipynb +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.0_introduction/introduction.ipynb @@ -34,6 +34,11 @@ "leveraging confidential computing's VM-based trust execution environment (TEE), NVIDIA FLARE will enable end-to-end confidential federated AI. We will brief touch on it in this chapter. The details will be added in the future. \n", "\n", "\n", + "* **Communication Security**\n", + "\n", + "Use of Secure Protocols – TLS for secure transmission. FLARE support both mutual TLS (mTLS) as well normal TLS with signed message \n", + "\n", + "\n", "\n", "\n", "\n", diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.1_security_architecture/Seurity_architecture.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.1_security_architecture/Seurity_architecture.ipynb index 96eedd8e1d..46ed5fd54a 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.1_security_architecture/Seurity_architecture.ipynb +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.1_security_architecture/Seurity_architecture.ipynb @@ -72,8 +72,8 @@ "\n", "## Site Policy Management\n", "\n", - " see [section 6.3](../06.3_site_security/site_specific_security.ipynb)\n", - "\n", + " see [section 6.3](../06.3_site_security/site_specific_security.ipynb) for local site-specific authentication and authorization as well local site-specific security and privacy polcies. \n", + " \n", "## Communication Security\n", " see TODO \n", "\n", diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.2_identity_security/identity_security.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.2_identity_security/identity_security.ipynb index 61ccad3c1b..b1d3a83d88 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.2_identity_security/identity_security.ipynb +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.2_identity_security/identity_security.ipynb @@ -8,7 +8,7 @@ "\n", "## Authentication\n", "\n", - "NVFLARE’s authentication model is based on Public Key Infrastructure (PKI) technology:\n", + "NVFLARE’s authentication model is based on Public Key Infrastructure (PKI) technology\n", "\n", "For the FL project, the Project Admin uses the Provisioning Tool to create a Root CA with a self-signed root certificate. This Root CA will be used to issue all other certs needed by communicating parties.\n", "\n", diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/README.md b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/README.md similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/README.md rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/README.md diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/admin/local/custom/admin_auth.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/admin/local/custom/admin_auth.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/admin/local/custom/admin_auth.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/admin/local/custom/admin_auth.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/admin/local/resources.json b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/admin/local/resources.json similarity index 77% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/admin/local/resources.json rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/admin/local/resources.json index 0b81889308..bbd34fba77 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/admin/local/resources.json +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/admin/local/resources.json @@ -6,7 +6,7 @@ "path": "admin_auth.AdminAuth", "args": { "orgs": { - "site-a": "http://localhost:8080/realms/myrealm/protocol/openid-connect/token" + "site-1": "http://localhost:8080/realms/myrealm/protocol/openid-connect/token" } } } diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/data/download.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/data/download.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/data/download.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/data/download.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/fl_jobs.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/fl_jobs.py new file mode 100644 index 0000000000..458c8bed50 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/fl_jobs.py @@ -0,0 +1,53 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os + +from src.fedavg import FedAvg +from src.network import SimpleNetwork + +from nvflare.job_config.api import FedJob +from nvflare.job_config.script_runner import ScriptRunner + +if __name__ == "__main__": + num_clients = 2 + num_rounds = 2 + job_name = "fedavg" + train_script = "src/client.py" + config_dir = "/tmp/nvflare/jobs/workdir" + + + job = FedJob(name = job_name, min_clients = num_clients) + controller = FedAvg( + stop_cond = "accuracy > 25", + save_filename = "global_model.pt", + initial_model = SimpleNetwork(), + num_clients = num_clients, + num_rounds = num_rounds, + ) + + job.to_server(controller) + + # Add clients + for i in range(num_clients): + executor = ScriptRunner(script=train_script, script_args="") + job.to(executor, f"site-{i+1}") + + job_config_dir = os.path.join(config_dir, job_name) + print(f"job-config for {job_name} is at ",job_config_dir) + job.export_job(config_dir) + # job.simulator_run(config_dir) + + diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/requirements.txt b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/requirements.txt similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/requirements.txt rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/requirements.txt diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/client.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/client.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/client.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/client.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/fedavg.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/fedavg.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/fedavg.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/fedavg.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/network.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/network.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/src/network.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code/src/network.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/edit_site_local_resources.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/edit_site_local_resources.py new file mode 100644 index 0000000000..b2f6a4cbfb --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/edit_site_local_resources.py @@ -0,0 +1,65 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import json +import sys +import os + + +def get_security_handler() -> dict: + return json.loads( + """ + { + "id": "security_handler", + "path": "keycloak_security_handler.CustomSecurityHandler" + } + """ + ) + + +def add_components_to_json( + input_file_path, output_file_path, site: str, receiving: bool = False, streaming_to_server: bool = False + ): + try: + with open(input_file_path, "r") as file: + data = json.load(file) + except (FileNotFoundError, json.JSONDecodeError): + print(f"Error: Unable to read or parse JSON file: {input_file_path}") + return + + new_components = [get_security_handler()] + + # Append new components to the list + data["components"].extend(new_components) + + # Write the updated JSON back to the file + with open(output_file_path, "w") as file: + json.dump(data, file, indent=4) + + print(f"Successfully generate file: '{output_file_path}'.") + + +if __name__ == "__main__": + + site_name = sys.argv[1] + project_root_dir = sys.argv[2] + + + print(site_name, project_root_dir) + + input_file_path = os.path.join(project_root_dir, site_name, "local", "resources.json.default") + output_file_path = os.path.join(project_root_dir, site_name, "local", "resources.json") + + add_components_to_json(input_file_path, output_file_path, site_name) \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/get_keycloak_access_token.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/get_keycloak_access_token.py new file mode 100644 index 0000000000..935176917d --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/get_keycloak_access_token.py @@ -0,0 +1,60 @@ +import requests +import os +import sys + +def save_access_token(access_token:str, destination_path): + + # Ensure the destination directory exists + os.makedirs(os.path.dirname(destination_path), exist_ok=True) + + with open(destination_path, "w") as f: + f.write(access_token) + print(f"Access token saved to {destination_path}") + + +def get_keycloak_acces_token(username, password, client_id,keycloak_url) -> str: + + + # Request payload + data = { + "username": username, + "password": password, + "grant_type": "password", + "client_id": client_id + } + + try: + # Make a POST request to get the access token + response = requests.post(keycloak_url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"}) + response_data = response.json() + + # Extract the access token + access_token = response_data.get("access_token") + + if not access_token: + print("Failed to retrieve access token.") + else: + return access_token + + except Exception as e: + print(f"Error fetching access token: {e}") + + +if __name__ == "__main__": + + # Define variables + keycloak_url = "http://localhost:8080/realms/master/protocol/openid-connect/token" + username = "admin" + password = "admin123" + client_id = "admin-cli" + destination_path = sys.argv[1] + + token = get_keycloak_acces_token(username=username, password=password, client_id= client_id, keycloak_url=keycloak_url) + + print("token=", token) + + save_access_token(token, destination_path=destination_path) + + + + diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml similarity index 62% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml index f86f414b04..26feecc5ec 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml @@ -2,10 +2,8 @@ version: '3.8' services: keycloak: - # image: quay.io/keycloak/keycloak:24.0.1 - image: bitnami/keycloak:24 + build: . container_name: keycloak - command: start-dev environment: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin123 @@ -14,9 +12,12 @@ services: volumes: - .:/opt/keycloak-setup depends_on: - - db - entrypoint: ["/bin/sh", "-c", "/opt/keycloak-setup/init.sh && /opt/keycloak/bin/kc.sh start-dev"] - + db: + condition: service_healthy + networks: + - bridge_network + + db: image: postgres:15 container_name: keycloak-db @@ -28,6 +29,17 @@ services: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U keycloak"] + interval: 10s + retries: 5 + start_period: 10s + networks: + - bridge_network volumes: pgdata: + +networks: + bridge_network: + driver: bridge \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/dockerfile b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/dockerfile new file mode 100644 index 0000000000..cac41467cd --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/dockerfile @@ -0,0 +1,18 @@ +FROM bitnami/keycloak:24 + +USER root + +# Install jq (and any other necessary dependencies) +RUN apt-get update && apt-get install -y jq + +# Set working directory +WORKDIR /opt/keycloak-setup + +# Copy the setup scripts to the container +COPY ./init.sh /opt/keycloak-setup/init.sh + +# Set permissions to ensure init.sh is executable +RUN chmod +x /opt/keycloak-setup/init.sh + +# Set the entrypoint +ENTRYPOINT ["/bin/sh", "-c", "/opt/bitnami/scripts/keycloak/run.sh & sleep 10 && /opt/keycloak-setup/init.sh && tail -f /dev/null"] \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/init.sh b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/init.sh similarity index 95% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/init.sh rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/init.sh index 7c9a7a9f74..59708ffbf9 100755 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/keycloak-setup/init.sh +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak-setup/init.sh @@ -1,3 +1,5 @@ +#!/bin/bash + # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,15 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash - # Wait for Keycloak to be ready echo "Waiting for Keycloak to be ready..." -until $(curl --output /dev/null --silent --head --fail http://keycloak:8080/realms/master); do +until curl -sf http://keycloak:8080/realms/master > /dev/null; do printf '.' sleep 5 done + echo "Keycloak is ready!" # Get Admin Token diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak_integration.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak_integration.ipynb new file mode 100644 index 0000000000..1c02e7788f --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/keycloak_integration.ipynb @@ -0,0 +1,362 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c8393461", + "metadata": {}, + "source": [ + "## Integration with external authentication system\n", + "\n", + "#### Overview\n", + "\n", + "In Federated Computing systems, many participating institutions already have their own in-house authentication systems. Instead of introducing a new authentication mechanism, we need to integrate with them. Note that this integration can be site-specific; in other words, each site may be different. Site-1 can be using OLAP, site-2 can use OAuth, and a third site can use something else.\n", + "\n", + "In this example, we demonstrate NVIDIA FLARE's event-based plugin-component that can be used to integrate any type of authentication/authorization mechanism, using open-source KeyCloak integration as an example.\n", + "\n", + "### Setup KeyCloak\n", + "\n", + "Before we start, we need to download and start the KeyCloa service. To do that, we create a [dockerfile](./examples/custom_client_side_auth_system_integration/keycloak-setup/dockerfile) and [docker-compose](./examples/custom_client_side_auth_system_integration/keycloak-setup/docker-compose.yml) file\n", + "\n", + "to start cd to ```custom_client_side_auth_system_integration/keycloak-setup``` directory and run the following command\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8dbd0c77", + "metadata": {}, + "outputs": [], + "source": [ + "%cd keycloak-setup\n", + "! docker compose up -d --build\n", + "%cd -\n" + ] + }, + { + "cell_type": "markdown", + "id": "bc6ccaa0", + "metadata": {}, + "source": [ + "#* check if the keycloak is ready \n", + "!docker ps\n", + "!docker logs keycloak \n", + "\n", + "you should see something like \n", + "\n", + "```\n", + " Keycloak is ready!\n", + " ...\n", + "\n", + " Setup completed!\n", + "```\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "dd304d80", + "metadata": {}, + "source": [ + "You can also check if the website and login with credential user = admin password = admin123" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd65e072", + "metadata": {}, + "outputs": [], + "source": [ + "http://localhost:8080\n" + ] + }, + { + "cell_type": "markdown", + "id": "be7f97c7", + "metadata": {}, + "source": [ + "### Setup KeyCloak Authentication Plugin\n", + "\n", + "This integration involves two plugins: \n", + "\n", + "* At admin client during job submision, we will require the login for the given site (site-1). We also need to get the access token to pass to the job context\n", + "* one at site local, we need to plugin for job authorization\n", + "\n", + "\n", + "#### Set up FL Client Job Authorization Configuration\n", + "\n", + "First, we need to overwrite the default local resources.json.default with resources.json to add the custom security check component\n", + "\n", + " {\n", + " \"id\": \"security_handler\",\n", + " \"path\": \"keycloak_security_handler.CustomSecurityHandler\"\n", + " }\n", + "\n", + "The \"keycloak_security_handler.CustomSecurityHandler\" is defined as " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d0da496", + "metadata": {}, + "outputs": [], + "source": [ + "!cat security/custom/keycloak_security_handler.py" + ] + }, + { + "cell_type": "markdown", + "id": "8cf35e71", + "metadata": {}, + "source": [ + "we also need to save the KeyCloak public_key in the `/tmp/nvflare/poc/example_project/prod_00/site-1/local/site-1/local/public_key.pem` file, with the following format:\n", + "\n", + "```\n", + "-----BEGIN PUBLIC KEY-----\n", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAre3kQxqOfTJ7LLRwlpotw47goqSsuyFOg9Ihx5IXDMbO8HTGuGQcdDVJaYJQYphfhp2qdw+1o6qVN2yPBxwiBWju/XZQMPbCXRBu2bVDffWJVMoelLDbr3uY9hCgYgmB7qYpDdNOmxb2+xIlg/x0q+vrRRMtdd8SGicvjg0mQSEEF4a7QOSwuDnwBX8+bMOXfyB5qQJlakNVND1Bc+MjDENkHLtImVowX9XZcz8M6Ap9Eq1z2agl6lmFxTLtZroTE6IQS/dFYPVy4rZ1Zuy5cvs/3j+SYzlplH/iP3qZs8UiKrTJMmfIuLmDbP3hEAOsEmQ/M3lRxnE4wuGxvel5rwIDAQAB\n", + "-----END PUBLIC KEY-----\n", + "```\n", + "\n", + "In the local/custom/resources.json config file, it contains the following additional security handler:\n", + "\n", + "```\n", + " {\n", + " \"id\": \"security_handler\",\n", + " \"path\": \"keycloak_security_handler.CustomSecurityHandler\"\n", + " }\n", + "```\n", + "\n", + "The CustomSecurityHandler in the custom/keycloak_security_handler.py contains the logic to validate the admin user's KeyCloak access token when the admin user submits a job, or scheduler picks up an already submitted job from the admin user. If the access token is invalid, the job will not be authorized to run.\n", + "\n", + "We can do this in the following code. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8551b25b", + "metadata": {}, + "outputs": [], + "source": [ + "# prepare poc\n", + "! echo y | nvflare poc prepare -n 2\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "636b2727", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "!python get_keycloak_access_token.py /tmp/nvflare/poc/example_project/prod_00/site-1/local/site-1/local/public_key.pem\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e8644a9", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "!python edit_site_local_resources.py site-1 /tmp/nvflare/poc/example_project/prod_00\n" + ] + }, + { + "cell_type": "markdown", + "id": "93dcf155", + "metadata": {}, + "source": [ + "\n", + "#### Set up Admin user authentication\n", + "\n", + "\n", + "In the local/custom/resources.json config file, it contains the following admin event handler. the \"orgs\" arg provides a list of site names, and it's corresponding KeyCloak access_token URLs:\n", + "\n", + "```\n", + " {\n", + " \"id\": \"auth\",\n", + " \"path\": \"admin_auth.AdminAuth\",\n", + " \"args\": {\n", + " \"orgs\": {\n", + " \"site-a\": \"http://localhost:8080/realms/myrealm/protocol/openid-connect/token\"\n", + " }\n", + " }\n", + " }\n", + "```\n", + "\n", + "The AdminAuth event handler in the custom/admin_auth.py has the logic to acquire the KeyCloak access tokens to each individual site. When the admin user submits a job, it will set the tokens in the FLContext.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89ca8b71", + "metadata": {}, + "outputs": [], + "source": [ + "! mkdir -p /tmp/nvflare/poc/example_project/prod_00/admin@nvidia.com/local/\n", + "! cp -r admin/local/* /tmp/nvflare/poc/example_project/prod_00/admin@nvidia.com/local/" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "2044c070", + "metadata": {}, + "outputs": [], + "source": [ + "! cp -r site/local/* /tmp/nvflare/poc/example_project/prod_00/site-1/local/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c48462ee", + "metadata": {}, + "outputs": [], + "source": [ + "! tree /tmp/nvflare/poc/example_project/prod_00/admin@nvidia.com/local/" + ] + }, + { + "cell_type": "markdown", + "id": "e6b36385", + "metadata": {}, + "source": [ + "### Test the setup \n", + "\n", + "start poc\n", + "\n", + "\n", + "#### Logging with Admin Console\n", + "\n", + "At the prompt, enter the user email `admin@nvidia.com`, and then provide the password to `site_1` KeyCloak.\n", + "\n", + "\n", + "#### Require authenticated admin user when running jobs\n", + "\n", + "With this system set up, the `site-1` will require only the authenticated admin user to be able to submit and run a job. `site-2` does not have this additional security requirement. Any admin user can submit and run the job.\n", + "\n", + "\n", + "##### Authenticated admin user\n", + "\n", + "* `admin@nvidia.com` should successfully authenticated to `site-1` KeyCloak system. The job is successfully submitted and run on both `site-1` and `site-2`.\n", + "\n", + "##### Un-authenticated admin user\n", + "\n", + "* If the wrong password is provided, or for some reason KeyCloak system is not available when starting the admin tool, or submitting the job, the job won't be able to run the `site-1`. \n", + "* `site-1` will show \"ERROR - Authorization failed\".but the job can successfully run on `site-2`.\n", + "* `list_jobs -d JOB_ID` command will show \"job_deploy_detail\" information of this job.\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "id": "40073347", + "metadata": {}, + "source": [ + "Let's try this out" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eadd3f5c", + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare the data\n", + "\n", + "! python code/data/download.py" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "58688bcd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/chester/projects/NVFlare/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/code\n", + "job-config for fedavg is at /tmp/nvflare/jobs/workdir/fedavg\n", + "/home/chester/projects/NVFlare/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration\n" + ] + } + ], + "source": [ + "# create job config\n", + "%cd code/\n", + "\n", + "! python ./fl_jobs.py\n", + "\n", + "%cd -" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "355c3624", + "metadata": {}, + "outputs": [], + "source": [ + "! nvflare simulator /tmp/nvflare/jobs/workdir/fedavg -w /tmp/nvflare/workspace/tmp" + ] + }, + { + "cell_type": "markdown", + "id": "5f3de6a0", + "metadata": {}, + "source": [ + "* Start POC without admin console\n", + "\n", + " ``` nvflare poc start -ex admin@nvidia.com```" + ] + }, + { + "cell_type": "markdown", + "id": "4228c715", + "metadata": {}, + "source": [ + "* Start POC admin console in separate terminal\n", + "\n", + " ``` nvflare poc start -p admin@nvidia.com```" + ] + }, + { + "cell_type": "markdown", + "id": "f7f2dbc4", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/site/local/custom/keycloak_security_handler.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/site/local/custom/keycloak_security_handler.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/site/local/custom/keycloak_security_handler.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/site/local/custom/keycloak_security_handler.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/site/local/resources.json b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/site/local/resources.json similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/site/local/resources.json rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_auth_system_integration/site/local/resources.json diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/client_side_security_check.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/client_side_security_check.ipynb new file mode 100644 index 0000000000..27696ea45c --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/client_side_security_check.ipynb @@ -0,0 +1,213 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e0053b6f", + "metadata": {}, + "source": [ + "## Client Side: Customized Job-level Auhtorization\n", + "\n", + "let's take look authorization on the client side\n", + "\n", + "**Setup**\n", + "\n", + "* `server`: NVFlare server\n", + "* `site-1`: Site-1 has a CustomSecurityHandler set up which does not allow the job \"secret-job\" to run. All other jobs will be able to deploy and run on site-1.\n", + "* `site-2`: Site-2 allows any job to be deployed and run.\n", + "\n", + "**Expectation**\n", + "* \"secret-job\" will be deployed and run on site-2 but not on site-1\n", + "\n", + "\n", + "What we will do: \n", + "\n", + "* install dependencies\n", + "* download data\n", + "* generate two job configs. We can use fl_jobs.py\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a9cd2a1", + "metadata": {}, + "outputs": [], + "source": [ + "# install dependencies\n", + "\n", + "! pip install -r code/requirements.txt\n", + "\n", + "# download data\n", + "\n", + "! python code/data/download.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c49a6ea6", + "metadata": {}, + "outputs": [], + "source": [ + "%cd code\n", + "\n", + "! python fl_jobs.py\n", + "\n", + "# change back\n", + "%cd - \n" + ] + }, + { + "cell_type": "markdown", + "id": "b06fce58", + "metadata": {}, + "source": [ + "Next we \n", + "* create a POC workspace and\n", + "* then install the customized securitry handler to site-1,\n", + "* edit site-1/local/resources.json to add security handler component \n", + "> note: \n", + " to simplify, we just copy the pre-edit resources.json to that location\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15ec4aee", + "metadata": {}, + "outputs": [], + "source": [ + "# prepare poc\n", + "! echo y | nvflare poc prepare -n 2\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "c1856aff", + "metadata": {}, + "outputs": [], + "source": [ + "# cp security handler and component config\n", + "!cp -r security/site-1/* /tmp/nvflare/poc/example_project/prod_00/site-1/local/.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b1699805", + "metadata": {}, + "source": [ + "Now we are ready to run the job. \n", + "\n", + "\n", + "* start poc\n", + "\n", + " Use a terminal ( note notebook cell) start the poc with the following command\n", + "\n", + " ```\n", + " nvflare poc start -ex admin@nvidia.com \n", + "\n", + " ```\n", + "\n", + " this bring up the FL system \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79e8d608", + "metadata": {}, + "outputs": [], + "source": [ + "# Submit jobs\n", + "# Assuming at this point FL system is already running via poc start command\n", + "\n", + "! nvflare job submit -j /tmp/nvflare/jobs/workdir/fedavg\n", + "\n", + "# The job should finish as expected" + ] + }, + { + "cell_type": "markdown", + "id": "c82c3ba6", + "metadata": {}, + "source": [ + "The fedavg job completed well. Now let's submit \"secret-job\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1872ca6", + "metadata": {}, + "outputs": [], + "source": [ + "# Submit jobs\n", + "# Assuming at this point FL system is already running via poc start command\n", + "\n", + "! nvflare job submit -j /tmp/nvflare/jobs/workdir/secret-job" + ] + }, + { + "cell_type": "markdown", + "id": "97480fa8", + "metadata": {}, + "source": [ + "you should get something like\n", + "\n", + "```\n", + "2025-02-02 20:31:03,494 - site_security - ERROR - Authorization failed. Reason: Not authorized to execute: check_resources\n", + "2025-02-02 20:31:03,496 - ServerEngine - ERROR - Client reply error: Not authorized to execute: check_resources\n", + "\n", + "```\n", + "\n", + "* Cleanup " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b0a4532", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "! nvflare poc stop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cf83674", + "metadata": {}, + "outputs": [], + "source": [ + "! nvflare poc clean" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/data/download.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/data/download.py new file mode 100644 index 0000000000..ebd8cfdc41 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/data/download.py @@ -0,0 +1,60 @@ +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This Dirichlet sampling strategy for creating a heterogeneous partition is adopted +# from FedMA (https://github.com/IBM/FedMA). + +# MIT License + +# Copyright (c) 2020 International Business Machines + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import argparse + +import torchvision.datasets as datasets + +# default dataset path +CIFAR10_ROOT = "/tmp/nvflare/data/cifar10" + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument("--dataset_path", type=str, default=CIFAR10_ROOT, nargs="?") + args = parser.parse_args() + return args + + +def main(args): + datasets.CIFAR10(root=args.dataset_path, train=True, download=True) + datasets.CIFAR10(root=args.dataset_path, train=False, download=True) + + +if __name__ == "__main__": + main(define_parser()) diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/fl_jobs.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/fl_jobs.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/code/fl_jobs.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/fl_jobs.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/requirements.txt b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/requirements.txt new file mode 100644 index 0000000000..57b4df2ed4 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/requirements.txt @@ -0,0 +1,3 @@ +torch +torchvision +tensorboard \ No newline at end of file diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/client.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/client.py new file mode 100644 index 0000000000..220559b3cf --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/client.py @@ -0,0 +1,193 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +import torch +import torch.nn as nn +import torch.optim as optim +import torchvision +import torchvision.transforms as transforms +from network import SimpleNetwork + +# (1) import nvflare client API +import nvflare.client as flare +from nvflare.app_common.app_constant import ModelName + +# (optional) set a fix place so we don't need to download everytime +CIFAR10_ROOT = "/tmp/nvflare/data/cifar10" + +# (optional) We change to use GPU to speed things up. +# if you want to use CPU, change DEVICE="cpu" +DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + + +def define_parser(): + parser = argparse.ArgumentParser() + parser.add_argument("--dataset_path", type=str, default=CIFAR10_ROOT, nargs="?") + parser.add_argument("--batch_size", type=int, default=4, nargs="?") + parser.add_argument("--learning_rate", type=float, default=0.001, nargs="?") + parser.add_argument("--num_workers", type=int, default=1, nargs="?") + parser.add_argument("--local_epochs", type=int, default=2, nargs="?") + parser.add_argument("--model_path", type=str, default=f"{CIFAR10_ROOT}/cifar_net.pth", nargs="?") + return parser.parse_args() + + +def main(): + # define local parameters + args = define_parser() + + dataset_path = args.dataset_path + batch_size = args.batch_size + num_workers = args.num_workers + local_epochs = args.local_epochs + model_path = args.model_path + lr = args.learning_rate + + transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + trainset = torchvision.datasets.CIFAR10(root=dataset_path, train=True, download=True, transform=transform) + trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=num_workers) + testset = torchvision.datasets.CIFAR10(root=dataset_path, train=False, download=True, transform=transform) + testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=num_workers) + + net = SimpleNetwork() + best_accuracy = 0.0 + + # wraps evaluation logic into a method to re-use for + # evaluation on both trained and received model + def evaluate(input_weights): + net = SimpleNetwork() + net.load_state_dict(input_weights) + # (optional) use GPU to speed things up + net.to(DEVICE) + + correct = 0 + total = 0 + # since we're not training, we don't need to calculate the gradients for our outputs + with torch.no_grad(): + for data in testloader: + # (optional) use GPU to speed things up + images, labels = data[0].to(DEVICE), data[1].to(DEVICE) + # calculate outputs by running images through the network + outputs = net(images) + # the class with the highest energy is what we choose as prediction + _, predicted = torch.max(outputs.data, 1) + total += labels.size(0) + correct += (predicted == labels).sum().item() + + return 100 * correct // total + + # (2) initialize NVFlare client API + flare.init() + + # (3) run continously when launch_once=true + while flare.is_running(): + + # (4) receive FLModel from NVFlare + input_model = flare.receive() + client_id = flare.get_site_name() + + # Based on different "task" we will do different things + # for "train" task (flare.is_train()) we use the received model to do training and/or evaluation + # and send back updated model and/or evaluation metrics, if the "train_with_evaluation" is specified as True + # in the config_fed_client we will need to do evaluation and include the evaluation metrics + # for "evaluate" task (flare.is_evaluate()) we use the received model to do evaluation + # and send back the evaluation metrics + # for "submit_model" task (flare.is_submit_model()) we just need to send back the local model + # (5) performing train task on received model + if flare.is_train(): + print(f"({client_id}) current_round={input_model.current_round}, total_rounds={input_model.total_rounds}") + + # (5.1) loads model from NVFlare + net.load_state_dict(input_model.params) + + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD(net.parameters(), lr=lr, momentum=0.9) + + # (optional) use GPU to speed things up + net.to(DEVICE) + # (optional) calculate total steps + steps = local_epochs * len(trainloader) + for epoch in range(local_epochs): # loop over the dataset multiple times + + running_loss = 0.0 + for i, data in enumerate(trainloader, 0): + # get the inputs; data is a list of [inputs, labels] + # (optional) use GPU to speed things up + inputs, labels = data[0].to(DEVICE), data[1].to(DEVICE) + + # zero the parameter gradients + optimizer.zero_grad() + + # forward + backward + optimize + outputs = net(inputs) + loss = criterion(outputs, labels) + loss.backward() + optimizer.step() + + # print statistics + running_loss += loss.item() + if i % 2000 == 1999: # print every 2000 mini-batches + print(f"({client_id}) [{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}") + running_loss = 0.0 + break + + print(f"({client_id}) Finished Training") + + # (5.2) evaluation on local trained model to save best model + local_accuracy = evaluate(net.state_dict()) + print(f"({client_id}) Evaluating local trained model. Accuracy on the 10000 test images: {local_accuracy}") + if local_accuracy > best_accuracy: + best_accuracy = local_accuracy + torch.save(net.state_dict(), model_path) + + # (5.3) evaluate on received model for model selection + accuracy = evaluate(input_model.params) + print( + f"({client_id}) Evaluating received model for model selection. Accuracy on the 10000 test images: {accuracy}" + ) + + # (5.4) construct trained FL model + output_model = flare.FLModel( + params=net.cpu().state_dict(), + metrics={"accuracy": accuracy}, + meta={"NUM_STEPS_CURRENT_ROUND": steps}, + ) + + # (5.5) send model back to NVFlare + flare.send(output_model) + + # (6) performing evaluate task on received model + elif flare.is_evaluate(): + accuracy = evaluate(input_model.params) + print(f"({client_id}) accuracy: {accuracy}") + flare.send(flare.FLModel(metrics={"accuracy": accuracy})) + + # (7) performing submit_model task to obtain best local model + elif flare.is_submit_model(): + model_name = input_model.meta["submit_model_name"] + if model_name == ModelName.BEST_MODEL: + try: + weights = torch.load(model_path) + net = SimpleNetwork() + net.load_state_dict(weights) + flare.send(flare.FLModel(params=net.cpu().state_dict())) + except Exception as e: + raise ValueError("Unable to load best model") from e + else: + raise ValueError(f"Unknown model_type: {model_name}") + + +if __name__ == "__main__": + main() diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/fedavg.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/fedavg.py new file mode 100644 index 0000000000..a63f0005bf --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/fedavg.py @@ -0,0 +1,158 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Callable, Dict, List, Optional + +import torch + +from nvflare.app_common.abstract.fl_model import FLModel +from nvflare.app_common.utils.math_utils import parse_compare_criteria +from nvflare.app_common.workflows.base_fedavg import BaseFedAvg +from nvflare.app_opt.pt.decomposers import TensorDecomposer +from nvflare.fuel.utils import fobs + + +class FedAvg(BaseFedAvg): + """Controller for FedAvg Workflow with Early Stopping and Model Selection. + + Args: + num_clients (int, optional): The number of clients. Defaults to 3. + num_rounds (int, optional): The total number of training rounds. Defaults to 5. + stop_cond (str, optional): early stopping condition based on metric. + string literal in the format of " " (e.g. "accuracy >= 80") + save_filename (str, optional): filename for saving model + initial_model (nn.Module, optional): initial PyTorch model + """ + + def __init__( + self, + *args, + stop_cond: str, + num_rounds: int, + save_filename: str = "FL_global_model.pt", + initial_model=None, + **kwargs, + ): + super().__init__(*args, **kwargs) + + self.stop_cond = stop_cond + self.num_rounds = num_rounds + + if stop_cond: + self.stop_condition = parse_compare_criteria(stop_cond) + else: + self.stop_condition = None + self.save_filename = save_filename + self.initial_model = initial_model + self.best_model: Optional[FLModel] = None + + def run(self) -> None: + self.info("Start FedAvg.") + + if self.initial_model: + # Use FOBS for serializing/deserializing PyTorch tensors (self.initial_model) + fobs.register(TensorDecomposer) + # PyTorch weights + initial_weights = self.initial_model.state_dict() + else: + initial_weights = {} + + model = FLModel(params=initial_weights) + + model.start_round = self.start_round + model.total_rounds = self.num_rounds + + for self.current_round in range(self.start_round, self.start_round + self.num_rounds): + self.info(f"Round {self.current_round} started.") + model.current_round = self.current_round + + clients = self.sample_clients(self.num_clients) + + results: List[FLModel] = self.send_model_and_wait(targets=clients, data=model) + aggregate_results = self.aggregate( + results, aggregate_fn=self.aggregate_fn + ) # using default aggregate_fn with `WeightedAggregationHelper`. Can overwrite self.aggregate_fn with signature Callable[List[FLModel], FLModel] + + model = self.update_model(model, aggregate_results) + + self.info(f"Round {self.current_round} global metrics: {model.metrics}") + + self.select_best_model(model) + + self.save_model(self.best_model, os.path.join(os.getcwd(), self.save_filename)) + + if self.should_stop(model.metrics, self.stop_condition): + self.info( + f"Stopping at round={self.current_round} out of total_rounds={self.num_rounds}. Early stop condition satisfied: {self.stop_condition}" + ) + break + + self.info("Finished FedAvg.") + + def should_stop(self, metrics: Optional[Dict] = None, stop_condition: Optional[str] = None): + if stop_condition is None or metrics is None: + return False + + key, target, op_fn = stop_condition + value = metrics.get(key, None) + + if value is None: + raise RuntimeError(f"stop criteria key '{key}' doesn't exists in metrics") + + return op_fn(value, target) + + def select_best_model(self, curr_model: FLModel): + if self.best_model is None: + self.best_model = curr_model + return + + if self.stop_condition: + metric, _, op_fn = self.stop_condition + if self.is_curr_model_better(self.best_model, curr_model, metric, op_fn): + self.info("Current model is new best model.") + self.best_model = curr_model + else: + self.best_model = curr_model + + def is_curr_model_better( + self, best_model: FLModel, curr_model: FLModel, target_metric: str, op_fn: Callable + ) -> bool: + curr_metrics = curr_model.metrics + if curr_metrics is None: + return False + if target_metric not in curr_metrics: + return False + + best_metrics = best_model.metrics + return op_fn(curr_metrics.get(target_metric), best_metrics.get(target_metric)) + + def save_model(self, model, filepath=""): + params = model.params + # PyTorch save + torch.save(params, filepath) + + # save FLModel metadata + model.params = {} + fobs.dumpf(model, filepath + ".metadata") + model.params = params + + def load_model(self, filepath=""): + # PyTorch load + params = torch.load(filepath) + + # load FLModel metadata + model = fobs.loadf(filepath + ".metadata") + model.params = params + return model diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/network.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/network.py new file mode 100644 index 0000000000..609b0b1581 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/code/src/network.py @@ -0,0 +1,37 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class SimpleNetwork(nn.Module): + def __init__(self): + super(SimpleNetwork, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = torch.flatten(x, 1) # flatten all dimensions except batch + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/security/site-1/custom/security_handler.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/security/site-1/custom/security_handler.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/security/site-1/custom/security_handler.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/security/site-1/custom/security_handler.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/security/site-1/resources.json b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/security/site-1/resources.json similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_job_level_authorization/security/site-1/resources.json rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_client_side_job_level_authorization/security/site-1/resources.json diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/edit_site_local_resources.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/edit_site_local_resources.py new file mode 100644 index 0000000000..40fb0e53e7 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/edit_site_local_resources.py @@ -0,0 +1,64 @@ +# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import json +import os +import sys + + +def get_security_handler() -> dict: + return json.loads( + """ + { + "id": "security_handler", + "path": "security_handler.ServerCustomSecurityHandler" + } + """ + ) + + +def add_components_to_json(input_file_path, output_file_path, site: str): + + try: + with open(input_file_path, "r") as file: + data = json.load(file) + except (FileNotFoundError, json.JSONDecodeError): + print("Error: Unable to read or parse JSON file.") + return + + if "components" not in data or not isinstance(data["components"], list): + print("Error: 'components' key not found or is not a list.") + return + + new_components = [get_security_handler()] + + # Append new components to the list + data["components"].extend(new_components) + + # Write the updated JSON back to the file + with open(output_file_path, "w") as file: + json.dump(data, file, indent=4) + + print(f"Successfully generate file: '{output_file_path}'.") + + +if __name__ == "__main__": + + site_name = sys.argv[1] + project_root_dir = sys.argv[2] + + input_file_path = os.path.join(project_root_dir, site_name, "local", "resources.json.default") + output_file_path = os.path.join(project_root_dir, site_name, "local", "resources.json") + add_components_to_json(input_file_path, output_file_path, site_name) diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/custom/security_handler.py b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/security/server/custom/security_handler.py similarity index 100% rename from examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/custom/security_handler.py rename to examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/security/server/custom/security_handler.py diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/server_side_security_plugin.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/server_side_security_plugin.ipynb new file mode 100644 index 0000000000..a8c5aa48d6 --- /dev/null +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/custom_server_side_authentication/server_side_security_plugin.ipynb @@ -0,0 +1,163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c1f37174", + "metadata": {}, + "source": [ + "# Server Side Customized Authenticaton\n", + "\n", + "In this example, we will see how do we designed a custom plugin with additional authentication check, \n", + "As result for two sites in POC, site-2 is NOT able to start and register to the server. It's blocked by the ServerCustomSecurityHandler logic during the client registration.\n", + "\n", + "## Define a server side security handler\n", + "\n", + "Notice the we the customized the handler raise NotAuthenticated(\"site_2 not allowed to register\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "235ed0fa", + "metadata": {}, + "outputs": [], + "source": [ + "!cat security/server/custom/security_handler.py" + ] + }, + { + "cell_type": "markdown", + "id": "58c8f45a", + "metadata": {}, + "source": [ + "To register this plugin handler, we need to add this component to the server site's local configuration\n", + "\n", + "by adding it to the components array \n", + "\n", + "```\n", + " components: [\n", + " ...\n", + " {\n", + " \"id\": \"security_handler\",\n", + " \"path\": \"security_handler.ServerCustomSecurityHandler\"\n", + " }\n", + " ] \n", + "``` \n", + "\n", + "In this example, we will copy \"custom\" folder and \n", + "\n", + "use python code ```edit_site_local_resources.py``` to create \"resources.json\" to the at site local directory\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5bfe22f", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "! echo y | nvflare poc prepare" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c63cb5a", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "!cp -r security/server/* /tmp/nvflare/poc/example_project/prod_00/server/local/.\n", + "!python edit_site_local_resources.py server /tmp/nvflare/poc/example_project/prod_00" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d425be8", + "metadata": {}, + "outputs": [], + "source": [ + "# double check\n", + "! tree /tmp/nvflare/poc/example_project/prod_00/server/local/ " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a3eeae9", + "metadata": {}, + "outputs": [], + "source": [ + "!cat /tmp/nvflare/poc/example_project/prod_00/server/local/resources.json" + ] + }, + { + "cell_type": "markdown", + "id": "eb6e964e", + "metadata": {}, + "source": [ + "Now, go to a terminal and try to start FL system with \n", + "\n", + "```\n", + "\n", + "nvflare poc start -ex admin@nvidia.com\n", + "```\n", + "\n", + "See what happens" + ] + }, + { + "cell_type": "markdown", + "id": "f33e482c", + "metadata": {}, + "source": [ + "You should see something like this: \n", + "\n", + "\n", + "```\n", + "2025-02-02 16:35:40,059 - Communicator - INFO - Trying to register with server ...\n", + "2025-02-02 16:35:40,060 - ServerCustomSecurityHandler - ERROR - [identity=server, run=?, peer=site-2, peer_run=?] - Exception when handling event \"_client_register_received\": NotAuthenticated: site-2 not allowed to register\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84ab1ecd", + "metadata": {}, + "outputs": [], + "source": [ + "# Clean up\n", + "! nvflare poc stop\n", + "! nvflare poc clean" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/project.yml b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/project.yml deleted file mode 100644 index cfc347eb22..0000000000 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_client_side_auth_system_integration/project.yml +++ /dev/null @@ -1,40 +0,0 @@ -api_version: 3 -name: keycloak-site-authentication -description: NVFlare example for 3rd party user authentication integration - -participants: - - name: server1 - type: server - org: org_a - fed_learn_port: 8002 - admin_port: 8003 - - name: site_a - type: client - org: org_a - - name: site_b - type: client - org: org_b - - - name: myuser@example.com - type: admin - org: org_a - role: project_admin - -# The same methods in all builders are called in their order defined in builders section -builders: - - path: nvflare.lighter.impl.workspace.WorkspaceBuilder - args: - template_file: master_template.yml - - path: nvflare.lighter.impl.template.TemplateBuilder - - path: nvflare.lighter.impl.static_file.StaticFileBuilder - args: - # config_folder can be set to inform NVIDIA FLARE where to get configuration - config_folder: config - overseer_agent: - path: nvflare.ha.dummy_overseer_agent.DummyOverseerAgent - overseer_exists: false - args: - sp_end_point: server1:8002:8003 - - - path: nvflare.lighter.impl.cert.CertBuilder - - path: nvflare.lighter.impl.signature.SignatureBuilder diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/resources.json b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/resources.json deleted file mode 100644 index 54989857df..0000000000 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/examples/custom_server_side_authentication/security/server/resources.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "format_version": 2, - "servers": [ - { - "admin_storage": "transfer", - "max_num_clients": 100, - "heart_beat_timeout": 600, - "num_server_workers": 4, - "download_job_url": "http://download.server.com/", - "compression": "Gzip" - } - ], - "snapshot_persistor": { - "path": "nvflare.app_common.state_persistors.storage_state_persistor.StorageStatePersistor", - "args": { - "uri_root": "/", - "storage": { - "path": "nvflare.app_common.storages.filesystem_storage.FilesystemStorage", - "args": { - "root_dir": "/tmp/nvflare/snapshot-storage", - "uri_root": "/" - } - } - } - }, - "components": [ - { - "id": "job_scheduler", - "path": "nvflare.app_common.job_schedulers.job_scheduler.DefaultJobScheduler", - "args": { - "max_jobs": 4 - } - }, - { - "id": "job_manager", - "path": "nvflare.apis.impl.job_def_manager.SimpleJobDefManager", - "args": { - "uri_root": "/tmp/nvflare/jobs-storage", - "job_store_id": "job_store" - } - }, - { - "id": "job_store", - "path": "nvflare.app_common.storages.filesystem_storage.FilesystemStorage" - }, - { - "id": "process_launcher", - "path": "nvflare.app_common.job_launcher.server_process_launcher.ServerProcessJobLauncher", - "args": {} - }, - { - "id": "log_receiver", - "path": "nvflare.app_common.logging.log_receiver.LogReceiver", - "args": {} - }, - { - "id": "security_handler", - "path": "security_handler.ServerCustomSecurityHandler" - } - - ] -} diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/site_specific_security.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/site_specific_security.ipynb index a3217c582b..13f321b533 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/site_specific_security.ipynb +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/chapter-6_Security_in_federated_compute_system/06.3_site_security/site_specific_security.ipynb @@ -25,43 +25,7 @@ "Because of the external pluginable authentication and authorization processes, the results of the processes could potentially cause the jobs to not be able to be deployed or run. When configuring and using these functions, the users need to be aware of the impact and know where to plug in the authentication and authorization check.\n", "\n", "### Event based pluginable authentication and authorization\n", - "The NVFlare event based solution supports site-specific authentication and federated job-level authorization. Users can provide and implement any sort of additional security checks by building and plugging in FLcomponents which listen to the appropriate events and provide custom authentication and authorization functions.\n", - "\n", - "\n", - "To use the site-specific security functions, write a custom Security implementation in the local/custom/security_handler.py, then configure it as a component in the site resources.json.\n", - "\n", - "```\n", - "from typing import Tuple\n", - "\n", - "from nvflare.apis.event_type import EventType\n", - "from nvflare.apis.fl_component import FLComponent\n", - "from nvflare.apis.fl_constant import FLContextKey\n", - "from nvflare.apis.fl_context import FLContext\n", - "from nvflare.apis.job_def import JobMetaKey\n", - "\n", - "\n", - "class CustomSecurityHandler(FLComponent):\n", - "\n", - " def handle_event(self, event_type: str, fl_ctx: FLContext):\n", - " if event_type == EventType.AUTHORIZE_COMMAND_CHECK:\n", - " result, reason = self.authorize(fl_ctx=fl_ctx)\n", - " if not result:\n", - " fl_ctx.set_prop(FLContextKey.AUTHORIZATION_RESULT, False, sticky=False)\n", - " fl_ctx.set_prop(FLContextKey.AUTHORIZATION_REASON, reason, sticky=False)\n", - "\n", - " def authorize(self, fl_ctx: FLContext) -> Tuple[bool, str]:\n", - " command = fl_ctx.get_prop(FLContextKey.COMMAND_NAME)\n", - " if command in [\"check_resources\"]:\n", - " security_items = fl_ctx.get_prop(FLContextKey.SECURITY_ITEMS)\n", - " job_meta = security_items.get(FLContextKey.JOB_META)\n", - " if job_meta.get(JobMetaKey.JOB_NAME) == \"FL Demo Job1\":\n", - " return False, f\"Not authorized to execute: {command}\"\n", - " else:\n", - " return True, \"\"\n", - " else:\n", - " return True, \"\"\n", - "\n", - "```" + "The NVFlare event based solution supports site-specific authentication and federated job-level authorization. Users can provide and implement any sort of additional security checks by building and plugging in FLcomponents which listen to the appropriate events and provide custom authentication and authorization functions.\n" ] }, { @@ -69,418 +33,17 @@ "id": "928772df", "metadata": {}, "source": [ - "# Site-specific Authentication & Authorization\n", - "\n", - "Lets look these mechanism via examples" - ] - }, - { - "cell_type": "markdown", - "id": "c1f37174", - "metadata": {}, - "source": [ - "## Server Side Customized Authenticaton\n", - "\n", - "In this example, we will see how do we designed a custom plugin with additional authentication check, \n", - "As result for two sites in POC, site-2 is NOT able to start and register to the server. It's blocked by the ServerCustomSecurityHandler logic during the client registration.\n", - "\n", - "### Define a server side security handler\n", - "\n", - "Notice the we the customized the handler raise NotAuthenticated(\"site_2 not allowed to register\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "235ed0fa", - "metadata": {}, - "outputs": [], - "source": [ - "!cat examples/custom_server_side_authentication/security/server/custom/security_handler.py" - ] - }, - { - "cell_type": "markdown", - "id": "58c8f45a", - "metadata": {}, - "source": [ - "To register this plugin handler, we need to add this component to the server site's local configuration\n", - "\n", - "by adding it to the components array \n", - "\n", - "```\n", - " components: [\n", - " ...\n", - " {\n", - " \"id\": \"security_handler\",\n", - " \"path\": \"security_handler.ServerCustomSecurityHandler\"\n", - " }\n", - " ] \n", - "``` \n", - "\n", - "In this example, we have made a edit resources.json file and will copy both \"custom\" folder and \"resources.json\" to the local directory\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4c63cb5a", - "metadata": {}, - "outputs": [], - "source": [ - "! echo y | nvflare poc prepare\n", - "\n", - "!cp -r examples/custom_server_side_authentication/security/server/* /tmp/nvflare/poc/example_project/prod_00/server/local/.\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "eb6e964e", - "metadata": {}, - "source": [ - "Now, go to a terminal and try to start FL system with \n", - "\n", - "```\n", - "\n", - "nvflare poc start -ex admin@nvidia.com\n", - "```\n", - "\n", - "See what happens" - ] - }, - { - "cell_type": "markdown", - "id": "f33e482c", - "metadata": {}, - "source": [ - "You should see something like this: \n", - "\n", - "\n", - "```\n", - "2025-02-02 16:35:40,059 - Communicator - INFO - Trying to register with server ...\n", - "2025-02-02 16:35:40,060 - ServerCustomSecurityHandler - ERROR - [identity=server, run=?, peer=site-2, peer_run=?] - Exception when handling event \"_client_register_received\": NotAuthenticated: site-2 not allowed to register\n", - "\n", - "\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "84ab1ecd", - "metadata": {}, - "outputs": [], - "source": [ - "# Clean up\n", - "\n", - "! nvflare poc clean" - ] - }, - { - "cell_type": "markdown", - "id": "e0053b6f", - "metadata": {}, - "source": [ - "## Client Side: Customized Job-level Auhtorization\n", - "\n", - "Next, let's take look authorization on the client side\n", - "\n", - "**Setup**\n", - "\n", - "* `server`: NVFlare server\n", - "* `site-1`: Site-1 has a CustomSecurityHandler set up which does not allow the job \"secret-job\" to run. All other jobs will be able to deploy and run on site-1.\n", - "* `site-2`: Site-2 allows any job to be deployed and run.\n", - "\n", - "**Expectation**\n", - "* \"secret-job\" will be deployed and run on site-2 but not on site-1\n", - "\n", - "\n", - "What we will do: \n", - "\n", - "* install dependencies\n", - "* download data\n", - "* generate two job configs. We can use fl_jobs.py\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2a9cd2a1", - "metadata": {}, - "outputs": [], - "source": [ - "# install dependencies\n", - "\n", - "! pip install -r examples/custom_client_side_job_level_authorization/code/requirements.txt\n", - "\n", - "# download data\n", - "\n", - "! python examples/custom_client_side_job_level_authorization/code/data/download.py" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c49a6ea6", - "metadata": {}, - "outputs": [], - "source": [ - "%cd examples/custom_client_side_job_level_authorization/code\n", - "\n", - "! python fl_jobs.py\n", - "\n", - "# change back\n", - "%cd - \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e78eda0f", - "metadata": {}, - "outputs": [], - "source": [ - "!cat /tmp/nvflare/jobs/workdir/fedavg/app_site-1/config/config_fed_client.json " - ] - }, - { - "cell_type": "markdown", - "id": "b06fce58", - "metadata": {}, - "source": [ - "Next we \n", - "* create a POC workspace and\n", - "* then install the customized securitry handler to site-1,\n", - "* edit site-1/local/resources.json to add security handler component \n", - "> note: \n", - " to simplify, we just copy the pre-edit resources.json to that location\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15ec4aee", - "metadata": {}, - "outputs": [], - "source": [ - "# prepare poc\n", - "! echo y | nvflare poc prepare\n" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "id": "c1856aff", - "metadata": {}, - "outputs": [], - "source": [ - "# cp security handler and component config\n", - "!cp -r examples/custom_client_side_job_level_authorization/security/site-1/* /tmp/nvflare/poc/example_project/prod_00/site-1/local/.\n" - ] - }, - { - "cell_type": "markdown", - "id": "b1699805", - "metadata": {}, - "source": [ - "Now we are ready to run the job. \n", - "\n", - "\n", - "* start poc\n", - "\n", - " Use a terminal ( note notebook cell) start the poc with the following command\n", - "\n", - " ```\n", - " nvflare poc start -ex admin@nvidia.com \n", - "\n", - " ```\n", - "\n", - " this bring up the FL system \n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "79e8d608", - "metadata": {}, - "outputs": [], - "source": [ - "# Submit jobs\n", - "# Assuming at this point FL system is already running via poc start command\n", - "\n", - "! nvflare job submit -j /tmp/nvflare/jobs/workdir/fedavg\n", - "\n", - "# The job should finish as expected" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1872ca6", - "metadata": {}, - "outputs": [], - "source": [ - "# Submit jobs\n", - "# Assuming at this point FL system is already running via poc start command\n", - "\n", - "! nvflare job submit -j /tmp/nvflare/jobs/workdir/secret-job" - ] - }, - { - "cell_type": "markdown", - "id": "97480fa8", - "metadata": {}, - "source": [ - "you should get something like\n", - "\n", - "```\n", - "2025-02-02 20:31:03,494 - site_security - ERROR - Authorization failed. Reason: Not authorized to execute: check_resources\n", - "2025-02-02 20:31:03,496 - ServerEngine - ERROR - Client reply error: Not authorized to execute: check_resources\n", - "\n", - "```\n", - "\n", - "* Cleanup " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b0a4532", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "! nvflare poc stop" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "878b737d", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8cf83674", - "metadata": {}, - "outputs": [], - "source": [ - "! nvflare poc clean" - ] - }, - { - "cell_type": "markdown", - "id": "c8393461", - "metadata": {}, - "source": [ - "## Client Side: Integration with authentication system\n", - "\n", - "#### Overview\n", - "\n", - "In Federated Computing systems, many participating institutions already have their own in-house authentication systems. Instead of introducing a new authentication mechanism, we need to integrate with them. Note that this integration can be site-specific; in other words, each site may be different. Site-1 can be using OLAP, site-2 can use OAuth, and a third site can use something else.\n", - "\n", - "In this example, we demonstrate NVIDIA FLARE's event-based plugin-component that can be used to integrate any type of authentication/authorization mechanism, using open-source KeyCloak integration as an example.\n", - "\n", - "### Setup KeyCloak\n", - "\n", - "\n", - "* Start the KeyCLoak by running \"bin/kc.sh start-dev\"\n", - "* Set up the realm called \"myrealm\"\n", - "* Setup the user \"myuser@example.com\"\n", - "* Add the client \"myclient\"\n", - "* The KeyCLoak will be running at http://localhost:8080/\n", - "\n", - " -->\n", - "\n", - "> pre-requisits\n", - "* need to install JDK first\n", - "* skip this step if you already have JDK installed \n", - "\n", - "#### JDK Installatioon \n", - "\n", - "``` \n", - "sudo apt install -y openjdk-21-jdk\n", - "\n", - "java -version\n", - "javac -version\n", - "\n", - "```\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "8dbd0c77", - "metadata": {}, - "outputs": [], - "source": [ - "VERSION = \"26.1.0\"\n", - "INSTALL_DIR = \"/tmp/keycloak\"\n", - "DOWNLOAD_URL = f\"https://github.com/keycloak/keycloak/releases/download/{VERSION}/keycloak-{VERSION}.zip\"\n", - "ZIP_PATH = f\"/tmp/keycloak-{VERSION}.zip\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc6ccaa0", - "metadata": {}, - "outputs": [], - "source": [ - "# Download Keycloak\n", - "\n", - "!curl -Lo {ZIP_PATH} {DOWNLOAD_URL}\n", - "! ls -al /tmp/keycloak-*\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fd65e072", - "metadata": {}, - "outputs": [], - "source": [ - "! echo A | unzip {ZIP_PATH} -d /tmp" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "23187fd4", - "metadata": {}, - "outputs": [], - "source": [ - "! mv /tmp/keycloak-{VERSION} {INSTALL_DIR}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60a09dca", - "metadata": {}, - "outputs": [], - "source": [ - "# setup admin user\n", + "Lets look these mechanism via \n", "\n", - "!cd {INSTALL_DIR} && ./bin/kc.sh create-admin --user admin --password admin123\n" + "* [Customized Server Side Security Check](./custom_server_side_authentication/server_side_security_plugin.ipynb)\n", + "* [Customized Client side job-level check](./custom_client_side_job_level_authorization/client_side_security_check.ipynb)\n", + "* [client side 3rd party authentication integration](./custom_client_side_auth_system_integration/keycloak_integration.ipynb)\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "7d0da496", + "id": "fa94cace", "metadata": {}, "outputs": [], "source": [] @@ -488,7 +51,7 @@ ], "metadata": { "kernelspec": { - "display_name": "nvflare_env", + "display_name": "Python 3", "language": "python", "name": "python3" }, diff --git a/examples/tutorials/self-paced-training/part-3_security_and_privacy/part-3_introduction.ipynb b/examples/tutorials/self-paced-training/part-3_security_and_privacy/part-3_introduction.ipynb index 8929aaf53c..ca7c151d09 100644 --- a/examples/tutorials/self-paced-training/part-3_security_and_privacy/part-3_introduction.ipynb +++ b/examples/tutorials/self-paced-training/part-3_security_and_privacy/part-3_introduction.ipynb @@ -11,9 +11,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "[Chapter 3.1 Privacy in Federated Learning](./chapter-3.1_Privacy_In_Federated_Learning/03.1.0_introduction.ipynb)\n", + "[Chapter 3.1 Privacy in Federated Learning](./chapter-5_Privacy_In_Federated_Learning/05.0_introduction/introduction.ipynb)\n", + "\n", + "[Chapter 3.2 Security in Federated Computing System](./chapter-6_Security_in_federated_compute_system/06.0_introduction/introduction.ipynb)\n", "\n", - "[Chapter 3.2 Security in Federated Computing System](chapter-3.2_Security_in_federated_compute_system/03.2.0_introduction.ipynb)\n", "\n" ] },