From c8ff2533446ef3e47021e38c56ddc182f3571527 Mon Sep 17 00:00:00 2001 From: Matteo Piano Date: Mon, 23 Sep 2024 05:30:54 -0700 Subject: [PATCH] local-run: add option to authenticate as the calling user --- paasta_tools/cli/cmds/local_run.py | 39 +++++++++++++++++------- paasta_tools/cli/utils.py | 10 ++++++ paasta_tools/utils.py | 4 +++ tests/cli/test_cmds_local_run.py | 8 +++++ yelp_package/extra_requirements_yelp.txt | 3 ++ 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/paasta_tools/cli/cmds/local_run.py b/paasta_tools/cli/cmds/local_run.py index 3905b6921e..da3b88a0f3 100755 --- a/paasta_tools/cli/cmds/local_run.py +++ b/paasta_tools/cli/cmds/local_run.py @@ -39,6 +39,7 @@ from paasta_tools.cli.utils import figure_out_service_name from paasta_tools.cli.utils import get_instance_config from paasta_tools.cli.utils import get_service_auth_token +from paasta_tools.cli.utils import get_sso_service_auth_token from paasta_tools.cli.utils import lazy_choices_completer from paasta_tools.cli.utils import list_instances from paasta_tools.cli.utils import pick_random_port @@ -507,17 +508,6 @@ def add_subparser(subparsers): required=False, default=False, ) - list_parser.add_argument( - "--use-service-auth-token", - help=( - "Acquire service authentication token for the underlying instance," - " and set it in the container environment" - ), - action="store_true", - dest="use_service_auth_token", - required=False, - default=False, - ) list_parser.add_argument( "--sha", help=( @@ -540,6 +530,29 @@ def add_subparser(subparsers): "Same as the -v / --volume parameter to docker run: hostPath:containerPath[:mode]" ), ) + service_auth_group = list_parser.add_mutually_exclusive_group() + service_auth_group.add_argument( + "--use-service-auth-token", + help=( + "Acquire service authentication token for the underlying instance," + " and set it in the container environment" + ), + action="store_true", + dest="use_service_auth_token", + required=False, + default=False, + ) + service_auth_group.add_argument( + "--use-sso-service-auth-token", + help=( + "Acquire service authentication token from SSO provider," + " and set it in the container environment" + ), + action="store_true", + dest="use_sso_service_auth_token", + required=False, + default=False, + ) list_parser.set_defaults(command=paasta_local_run) @@ -830,6 +843,7 @@ def run_docker_container( use_okta_role=False, assume_role_aws_account: Optional[str] = None, use_service_auth_token: bool = False, + use_sso_service_auth_token: bool = False, ): """docker-py has issues running a container with a TTY attached, so for consistency we execute 'docker run' directly in both interactive and @@ -921,6 +935,8 @@ def run_docker_container( if use_service_auth_token: environment["YELP_SVC_AUTHZ_TOKEN"] = get_service_auth_token() + elif use_sso_service_auth_token: + environment["YELP_SVC_AUTHZ_TOKEN"] = get_sso_service_auth_token() local_run_environment = get_local_run_environment_vars( instance_config=instance_config, port0=chosen_port, framework=framework @@ -1271,6 +1287,7 @@ def configure_and_run_docker_container( assume_role_aws_account=assume_role_aws_account, use_okta_role=args.use_okta_role, use_service_auth_token=args.use_service_auth_token, + use_sso_service_auth_token=args.use_sso_service_auth_token, ) diff --git a/paasta_tools/cli/utils.py b/paasta_tools/cli/utils.py index 979fbff996..b05240e1e4 100644 --- a/paasta_tools/cli/utils.py +++ b/paasta_tools/cli/utils.py @@ -82,6 +82,7 @@ from vault_tools.paasta_secret import get_client as get_vault_client from vault_tools.paasta_secret import get_vault_url from vault_tools.paasta_secret import get_vault_ca + from okta_auth import get_and_cache_jwt_default except ImportError: def get_vault_client(url: str, capath: str) -> None: @@ -93,6 +94,9 @@ def get_vault_url(ecosystem: str) -> str: def get_vault_ca(ecosystem: str) -> str: return "" + def get_and_cache_jwt_default(client_id: str) -> str: + return "" + log = logging.getLogger(__name__) @@ -1132,3 +1136,9 @@ def get_service_auth_token() -> str: ) response = vault_client.secrets.identity.generate_signed_id_token(name=vault_role) return response["data"]["token"] + + +def get_sso_service_auth_token() -> str: + """Generate an authentication token for the calling user from the Single Sign On provider""" + client_id = load_system_paasta_config().get_service_auth_sso_oidc_client_id() + return get_and_cache_jwt_default(client_id) diff --git a/paasta_tools/utils.py b/paasta_tools/utils.py index a5d585c8ca..73bff69a09 100644 --- a/paasta_tools/utils.py +++ b/paasta_tools/utils.py @@ -2044,6 +2044,7 @@ class SystemPaastaConfigDict(TypedDict, total=False): use_multiple_log_readers: Optional[List[str]] service_auth_token_settings: ProjectedSAVolume service_auth_vault_role: str + service_auth_sso_oidc_client_id: str always_authenticating_services: List[str] vitess_images: Dict superregion_to_region_mapping: Dict @@ -2764,6 +2765,9 @@ def get_service_auth_token_volume_config(self) -> ProjectedSAVolume: def get_service_auth_vault_role(self) -> str: return self.config_dict.get("service_auth_vault_role", "service_authz") + def get_service_auth_sso_oidc_client_id(self) -> str: + return self.config_dict.get("service_auth_sso_oidc_client_id", "") + def get_always_authenticating_services(self) -> List[str]: return self.config_dict.get("always_authenticating_services", []) diff --git a/tests/cli/test_cmds_local_run.py b/tests/cli/test_cmds_local_run.py index 6cf7c23693..d9871c1167 100644 --- a/tests/cli/test_cmds_local_run.py +++ b/tests/cli/test_cmds_local_run.py @@ -395,6 +395,7 @@ def test_configure_and_run_command_uses_cmd_from_config( args.assume_pod_identity = False args.use_okta_role = False args.use_service_auth_token = False + args.use_sso_service_auth_token = False mock_secret_provider_kwargs = { "vault_cluster_config": {}, @@ -438,6 +439,7 @@ def test_configure_and_run_command_uses_cmd_from_config( assume_role_aws_account=None, use_okta_role=False, use_service_auth_token=False, + use_sso_service_auth_token=False, ) @@ -473,6 +475,7 @@ def test_configure_and_run_uses_bash_by_default_when_interactive( args.assume_pod_identity = False args.use_okta_role = False args.use_service_auth_token = False + args.use_sso_service_auth_token = False return_code = configure_and_run_docker_container( docker_client=mock_docker_client, @@ -515,6 +518,7 @@ def test_configure_and_run_uses_bash_by_default_when_interactive( assume_pod_identity=False, use_okta_role=False, use_service_auth_token=False, + use_sso_service_auth_token=False, ) @@ -556,6 +560,7 @@ def test_configure_and_run_pulls_image_when_asked( args.assume_pod_identity = False args.use_okta_role = False args.use_service_auth_token = False + args.use_sso_service_auth_token = False return_code = configure_and_run_docker_container( docker_client=mock_docker_client, @@ -600,6 +605,7 @@ def test_configure_and_run_pulls_image_when_asked( assume_role_aws_account="dev", use_okta_role=False, use_service_auth_token=False, + use_sso_service_auth_token=False, ) @@ -637,6 +643,7 @@ def test_configure_and_run_docker_container_defaults_to_interactive_instance( args.assume_pod_identity = False args.use_okta_role = False args.use_service_auth_token = False + args.use_sso_service_auth_token = False mock_config = mock.create_autospec(AdhocJobConfig) mock_get_default_interactive_config.return_value = mock_config @@ -681,6 +688,7 @@ def test_configure_and_run_docker_container_defaults_to_interactive_instance( assume_role_aws_account="dev", use_okta_role=False, use_service_auth_token=False, + use_sso_service_auth_token=False, ) diff --git a/yelp_package/extra_requirements_yelp.txt b/yelp_package/extra_requirements_yelp.txt index 25c77c5fa5..3774624bf4 100644 --- a/yelp_package/extra_requirements_yelp.txt +++ b/yelp_package/extra_requirements_yelp.txt @@ -16,11 +16,14 @@ monk==1.3.0 # yelp-clog dependency mrjob==0.7.4 # scribereader dependency named-decorator==0.1.4 # yelp-profiling dependency ndg-httpsclient==0.4.3 # vault-tools dependency +okta-auth==1.0.1 pycparser==2.20 # vault-tools dependency pygpgme==0.3 # vault-tools dependency +pyjwt==2.9.0 # okta-auth dependency pyopenssl==19.0.0 # vault-tools dependency PySubnetTree==0.34 # yelp-lib dependency python-jsonschema-objects==0.3.1 # slo-transcoder dependency +saml-helper==2.3.3 # okta-auth dependency scribereader==1.1.1 signalform-tools==0.0.16 # slo-transcoder dependency slo-transcoder==3.3.0