From 7c7ee0c64b477f60a285378302a3e89ee1837b25 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 17 Sep 2025 22:09:06 +0200 Subject: [PATCH 01/12] DRIVERS-3131: Provide AWS scripts with no URI credentials --- .evergreen/auth_aws/aws_common.py | 329 +++++++++++++++++++++++ .evergreen/auth_aws/aws_tester.py | 296 +------------------- .evergreen/auth_aws/aws_tester_no_uri.py | 19 ++ 3 files changed, 349 insertions(+), 295 deletions(-) create mode 100644 .evergreen/auth_aws/aws_common.py create mode 100755 .evergreen/auth_aws/aws_tester_no_uri.py diff --git a/.evergreen/auth_aws/aws_common.py b/.evergreen/auth_aws/aws_common.py new file mode 100644 index 00000000..e7376437 --- /dev/null +++ b/.evergreen/auth_aws/aws_common.py @@ -0,0 +1,329 @@ +""" +Script for testing MONGDOB-AWS authentication. +""" + +import argparse +import json +import logging +import os +import random +import subprocess +import sys +from functools import partial +from pathlib import Path +from urllib.parse import quote_plus + +from pymongo import MongoClient +from pymongo.errors import OperationFailure + +HERE = Path(__file__).absolute().parent +LOGGER = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") + + +def join(*parts): + return os.path.join(*parts).replace(os.sep, "/") + + +sys.path.insert(0, str(HERE / "lib")) +from util import get_key as _get_key + +ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*" +ASSUMED_WEB_ROLE = "arn:aws:sts::857654397073:assumed-role/webIdentityTestRole/*" + +# This varies based on hosting EC2 as the account id and role name can vary +AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/evergreen_task_hosts_instance_role_production/*" +_USE_AWS_SECRETS = False + +try: + with (HERE / "aws_e2e_setup.json").open() as fid: + CONFIG = json.load(fid) + get_key = partial(_get_key, uppercase=False) +except FileNotFoundError: + CONFIG = os.environ.copy() + get_key = partial(_get_key, uppercase=True) + + +def run(args, env): + """Run a python command in a subprocess.""" + env.update(os.environ.copy()) + return subprocess.run([sys.executable, *args], env=env, check=False).returncode + + +def create_user(user, kwargs): + """Create a user and verify access.""" + LOGGER.info("Creating user %s", user) + client = MongoClient(username="bob", password="pwd123") + db = client["$external"] + try: + db.command(dict(createUser=user, roles=[{"role": "read", "db": "aws"}])) + except OperationFailure as e: + if "already exists" not in e.details["errmsg"]: + raise + client.close() + + # Verify access. + client = MongoClient(authMechanism="MONGODB-AWS", **kwargs) + client.aws.command("count", "test") + client.close() + + +def setup_assume_role(): + from aws_assume_role import _assume_role + + # Assume the role to get temp creds. + os.environ["AWS_ACCESS_KEY_ID"] = CONFIG[get_key("iam_auth_assume_aws_account")] + os.environ["AWS_SECRET_ACCESS_KEY"] = CONFIG[ + get_key("iam_auth_assume_aws_secret_access_key") + ] + + role_name = CONFIG[get_key("iam_auth_assume_role_name")] + creds = _assume_role(role_name, quiet=True) + with (HERE / "creds.json").open("w") as fid: + json.dump(creds, fid) + + # Create the user. + token = quote_plus(creds["SessionToken"]) + kwargs = dict( + username=creds["AccessKeyId"], + password=creds["SecretAccessKey"], + authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", + ) + create_user(ASSUMED_ROLE, kwargs) + return dict( + USER=kwargs["username"], + PASS=kwargs["password"], + SESSION_TOKEN=creds["SessionToken"], + ) + + +def setup_ec2(): + # Create the user. + from aws_assign_instance_profile import _assign_instance_policy + + _assign_instance_policy() + os.environ.pop("AWS_ACCESS_KEY_ID", None) + os.environ.pop("AWS_SECRET_ACCESS_KEY", None) + create_user(AWS_ACCOUNT_ARN, dict()) + return dict() + + +def setup_ecs(): + # Set up commands. + mongo_binaries = os.environ["MONGODB_BINARIES"] + project_dir = os.environ["PROJECT_DIRECTORY"] + base_command = f"{sys.executable} -u lib/container_tester.py" + run_prune_command = f"{base_command} -v remote_gc_services --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]}" + + # Get the appropriate task definition based on the version of Ubuntu. + with open("/etc/lsb-release") as fid: + text = fid.read() + if "jammy" in text: + task_definition = CONFIG.get( + get_key("iam_auth_ecs_task_definition_jammy"), None + ) + if task_definition is None: + raise ValueError('Please set "iam_auth_ecs_task_definition_jammy" variable') + elif "focal" in text: + task_definition = CONFIG.get( + get_key("iam_auth_ecs_task_definition_focal"), None + ) + # Fall back to previous task definition for backward compat. + if task_definition is None: + task_definition = CONFIG[get_key("iam_auth_ecs_task_definition")] + else: + raise ValueError("Unsupported ubuntu release") + run_test_command = f"{base_command} -d -v run_e2e_test --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]} --task_definition {task_definition} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_a')]} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_b')]} --security_group {CONFIG[get_key('iam_auth_ecs_security_group')]} --files {mongo_binaries}/mongod:/root/mongod {mongo_binaries}/mongosh:/root/mongosh lib/ecs_hosted_test.js:/root/ecs_hosted_test.js {project_dir}:/root --script lib/ecs_hosted_test.sh" + + # Pass in the AWS credentials as environment variables + # AWS_SHARED_CREDENTIALS_FILE does not work in evergreen for an unknown + # reason + env = dict( + AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ecs_account")], + AWS_SECRET_ACCESS_KEY=CONFIG[get_key("iam_auth_ecs_secret_access_key")], + ) + + # Prune other containers + subprocess.check_call(["/bin/sh", "-c", run_prune_command], env=env) + + # Run the test in a container + subprocess.check_call(["/bin/sh", "-c", run_test_command], env=env) + + return dict() + + +def setup_session_creds(): + # Set up the assume role user, and export the aws vars. + creds = setup_assume_role() + return dict( + AWS_ACCESS_KEY_ID=creds["USER"], + AWS_SECRET_ACCESS_KEY=creds["PASS"], + AWS_SESSION_TOKEN=creds["SESSION_TOKEN"], + ) + + +def setup_regular(): + # Create the user. + kwargs = dict( + username=CONFIG[get_key("iam_auth_ecs_account")], + password=CONFIG[get_key("iam_auth_ecs_secret_access_key")], + ) + create_user(CONFIG[get_key("iam_auth_ecs_account_arn")], kwargs) + + return dict(USER=kwargs["username"], PASS=kwargs["password"]) + + +def setup_env_creds(): + # Set up the regular user, but export the creds as environment vars. + creds = setup_regular() + return dict(AWS_ACCESS_KEY_ID=creds["USER"], AWS_SECRET_ACCESS_KEY=creds["PASS"]) + + +def setup_web_identity(): + from aws_assume_web_role import _assume_role_with_web_identity + + # Unassign the instance profile. + env = dict( + AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ec2_instance_account")], + AWS_SECRET_ACCESS_KEY=CONFIG[ + get_key("iam_auth_ec2_instance_secret_access_key") + ], + ) + ret = run(["lib/aws_unassign_instance_profile.py"], env) + if ret == 2: + raise RuntimeError("Request limit exceeded for AWS API") + + if ret != 0: + LOGGER.debug("return code was %s", ret) + raise RuntimeError( + "Failed to unassign an instance profile from the current machine" + ) + + token_file = os.environ.get( + "AWS_WEB_IDENTITY_TOKEN_FILE", CONFIG[get_key("iam_web_identity_token_file")] + ) + if os.name == "nt" and token_file.startswith("/tmp"): + token_file = token_file.replace("/tmp", "C:/cygwin/tmp/") + + # Handle the OIDC credentials. + env = dict( + IDP_ISSUER=CONFIG[get_key("iam_web_identity_issuer")], + IDP_JWKS_URI=CONFIG[get_key("iam_web_identity_jwks_uri")], + IDP_RSA_KEY=CONFIG[get_key("iam_web_identity_rsa_key")], + AWS_WEB_IDENTITY_TOKEN_FILE=token_file, + ) + + ret = run(["lib/aws_handle_oidc_creds.py", "token"], env) + if ret != 0: + raise RuntimeWarning("Failed to write the web token") + + # Assume the web role to get temp credentials. + os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file + role_arn = CONFIG[get_key("iam_auth_assume_web_role_name")] + os.environ["AWS_ROLE_ARN"] = role_arn + + creds = _assume_role_with_web_identity(True) + with (HERE / "creds.json").open("w") as fid: + json.dump(creds, fid) + + # Create the user. + token = quote_plus(creds["SessionToken"]) + kwargs = dict( + username=creds["AccessKeyId"], + password=creds["SecretAccessKey"], + authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", + ) + create_user(ASSUMED_WEB_ROLE, kwargs) + + return dict(AWS_WEB_IDENTITY_TOKEN_FILE=token_file, AWS_ROLE_ARN=role_arn) + + +def setup_eks_pod_identity(): + if "PROJECT_DIRECTORY" not in os.environ: + raise ValueError("Please define a PROJECT_DIRECTORY") + + test_path = ( + Path(os.environ["PROJECT_DIRECTORY"]) + / ".evergreen" + / "run-mongodb-aws-eks-test.sh" + ) + if not test_path.exists(): + raise ValueError(f"Please add the file {test_path}!") + + # Set the name for the deployment. + name = f"mongodb-{random.randrange(0, 32767)}" + try: + subprocess.check_call( + ["bash", HERE / "lib" / "eks-pod-setup.sh", name], cwd=HERE / "lib" + ) + finally: + # Tear down the EKS assets. + subprocess.check_call( + ["bash", HERE / "lib" / "eks-pod-teardown.sh", name], cwd=HERE / "lib" + ) + + return dict() + + +def handle_creds(creds: dict): + if "USER" in creds: + USER = quote_plus(creds["USER"]) + if "PASS" in creds: + PASS = quote_plus(creds["PASS"]) + MONGODB_URI = f"mongodb://{USER}:{PASS}@localhost" + else: + MONGODB_URI = f"mongodb://{USER}@localhost" + elif "MONGODB_URI" in creds: + MONGODB_URI = creds.pop("MONGODB_URI") + else: + MONGODB_URI = "mongodb://localhost" + MONGODB_URI = f"{MONGODB_URI}/aws?authMechanism=MONGODB-AWS" + if "SESSION_TOKEN" in creds: + SESSION_TOKEN = quote_plus(creds["SESSION_TOKEN"]) + MONGODB_URI = ( + f"{MONGODB_URI}&authMechanismProperties=AWS_SESSION_TOKEN:{SESSION_TOKEN}" + ) + with (HERE / "test-env.sh").open("w", newline="\n") as fid: + fid.write("#!/usr/bin/env bash\n\n") + fid.write("set +x\n") + for key, value in creds.items(): + if key in ["USER", "PASS", "SESSION_TOKEN"]: + value = quote_plus(value) # noqa: PLW2901 + fid.write(f"export {key}={value}\n") + fid.write(f'export MONGODB_URI="{MONGODB_URI}"\n') + + +def main(): + parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") + sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") + + run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") + run_assume_role_cmd.set_defaults(func=setup_assume_role) + + run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") + run_ec2_cmd.set_defaults(func=setup_ec2) + + run_ecs_cmd = sub.add_parser("ecs", help="ECS test") + run_ecs_cmd.set_defaults(func=setup_ecs) + + run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") + run_regular_cmd.set_defaults(func=setup_regular) + + run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") + run_session_creds_cmd.set_defaults(func=setup_session_creds) + + run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") + run_env_creds_cmd.set_defaults(func=setup_env_creds) + + run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") + run_web_identity_cmd.set_defaults(func=setup_web_identity) + + run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") + run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) + + args = parser.parse_args() + func_name = args.func.__name__.replace("setup_", "").replace("_", "-") + LOGGER.info("Running aws_tester.py with %s...", func_name) + creds = args.func() + handle_creds(creds) + LOGGER.info("Running aws_tester.py with %s... done.", func_name) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index 12bfe522..d11ad0d6 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -3,267 +3,9 @@ Script for testing MONGDOB-AWS authentication. """ -import argparse -import json -import logging -import os -import random -import subprocess -import sys -from functools import partial -from pathlib import Path from urllib.parse import quote_plus -from pymongo import MongoClient -from pymongo.errors import OperationFailure - -HERE = Path(__file__).absolute().parent -LOGGER = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") - - -def join(*parts): - return os.path.join(*parts).replace(os.sep, "/") - - -sys.path.insert(0, str(HERE / "lib")) -from util import get_key as _get_key - -ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*" -ASSUMED_WEB_ROLE = "arn:aws:sts::857654397073:assumed-role/webIdentityTestRole/*" - -# This varies based on hosting EC2 as the account id and role name can vary -AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/evergreen_task_hosts_instance_role_production/*" -_USE_AWS_SECRETS = False - -try: - with (HERE / "aws_e2e_setup.json").open() as fid: - CONFIG = json.load(fid) - get_key = partial(_get_key, uppercase=False) -except FileNotFoundError: - CONFIG = os.environ.copy() - get_key = partial(_get_key, uppercase=True) - - -def run(args, env): - """Run a python command in a subprocess.""" - env.update(os.environ.copy()) - return subprocess.run([sys.executable, *args], env=env, check=False).returncode - - -def create_user(user, kwargs): - """Create a user and verify access.""" - LOGGER.info("Creating user %s", user) - client = MongoClient(username="bob", password="pwd123") - db = client["$external"] - try: - db.command(dict(createUser=user, roles=[{"role": "read", "db": "aws"}])) - except OperationFailure as e: - if "already exists" not in e.details["errmsg"]: - raise - client.close() - - # Verify access. - client = MongoClient(authMechanism="MONGODB-AWS", **kwargs) - client.aws.command("count", "test") - client.close() - - -def setup_assume_role(): - from aws_assume_role import _assume_role - - # Assume the role to get temp creds. - os.environ["AWS_ACCESS_KEY_ID"] = CONFIG[get_key("iam_auth_assume_aws_account")] - os.environ["AWS_SECRET_ACCESS_KEY"] = CONFIG[ - get_key("iam_auth_assume_aws_secret_access_key") - ] - - role_name = CONFIG[get_key("iam_auth_assume_role_name")] - creds = _assume_role(role_name, quiet=True) - with (HERE / "creds.json").open("w") as fid: - json.dump(creds, fid) - - # Create the user. - token = quote_plus(creds["SessionToken"]) - kwargs = dict( - username=creds["AccessKeyId"], - password=creds["SecretAccessKey"], - authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", - ) - create_user(ASSUMED_ROLE, kwargs) - return dict( - USER=kwargs["username"], - PASS=kwargs["password"], - SESSION_TOKEN=creds["SessionToken"], - ) - - -def setup_ec2(): - # Create the user. - from aws_assign_instance_profile import _assign_instance_policy - - _assign_instance_policy() - os.environ.pop("AWS_ACCESS_KEY_ID", None) - os.environ.pop("AWS_SECRET_ACCESS_KEY", None) - create_user(AWS_ACCOUNT_ARN, dict()) - return dict() - - -def setup_ecs(): - # Set up commands. - mongo_binaries = os.environ["MONGODB_BINARIES"] - project_dir = os.environ["PROJECT_DIRECTORY"] - base_command = f"{sys.executable} -u lib/container_tester.py" - run_prune_command = f"{base_command} -v remote_gc_services --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]}" - - # Get the appropriate task definition based on the version of Ubuntu. - with open("/etc/lsb-release") as fid: - text = fid.read() - if "jammy" in text: - task_definition = CONFIG.get( - get_key("iam_auth_ecs_task_definition_jammy"), None - ) - if task_definition is None: - raise ValueError('Please set "iam_auth_ecs_task_definition_jammy" variable') - elif "focal" in text: - task_definition = CONFIG.get( - get_key("iam_auth_ecs_task_definition_focal"), None - ) - # Fall back to previous task definition for backward compat. - if task_definition is None: - task_definition = CONFIG[get_key("iam_auth_ecs_task_definition")] - else: - raise ValueError("Unsupported ubuntu release") - run_test_command = f"{base_command} -d -v run_e2e_test --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]} --task_definition {task_definition} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_a')]} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_b')]} --security_group {CONFIG[get_key('iam_auth_ecs_security_group')]} --files {mongo_binaries}/mongod:/root/mongod {mongo_binaries}/mongosh:/root/mongosh lib/ecs_hosted_test.js:/root/ecs_hosted_test.js {project_dir}:/root --script lib/ecs_hosted_test.sh" - - # Pass in the AWS credentials as environment variables - # AWS_SHARED_CREDENTIALS_FILE does not work in evergreen for an unknown - # reason - env = dict( - AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ecs_account")], - AWS_SECRET_ACCESS_KEY=CONFIG[get_key("iam_auth_ecs_secret_access_key")], - ) - - # Prune other containers - subprocess.check_call(["/bin/sh", "-c", run_prune_command], env=env) - - # Run the test in a container - subprocess.check_call(["/bin/sh", "-c", run_test_command], env=env) - - return dict() - - -def setup_session_creds(): - # Set up the assume role user, and export the aws vars. - creds = setup_assume_role() - return dict( - AWS_ACCESS_KEY_ID=creds["USER"], - AWS_SECRET_ACCESS_KEY=creds["PASS"], - AWS_SESSION_TOKEN=creds["SESSION_TOKEN"], - ) - - -def setup_regular(): - # Create the user. - kwargs = dict( - username=CONFIG[get_key("iam_auth_ecs_account")], - password=CONFIG[get_key("iam_auth_ecs_secret_access_key")], - ) - create_user(CONFIG[get_key("iam_auth_ecs_account_arn")], kwargs) - - return dict(USER=kwargs["username"], PASS=kwargs["password"]) - - -def setup_env_creds(): - # Set up the regular user, but export the creds as environment vars. - creds = setup_regular() - return dict(AWS_ACCESS_KEY_ID=creds["USER"], AWS_SECRET_ACCESS_KEY=creds["PASS"]) - - -def setup_web_identity(): - from aws_assume_web_role import _assume_role_with_web_identity - - # Unassign the instance profile. - env = dict( - AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ec2_instance_account")], - AWS_SECRET_ACCESS_KEY=CONFIG[ - get_key("iam_auth_ec2_instance_secret_access_key") - ], - ) - ret = run(["lib/aws_unassign_instance_profile.py"], env) - if ret == 2: - raise RuntimeError("Request limit exceeded for AWS API") - - if ret != 0: - LOGGER.debug("return code was %s", ret) - raise RuntimeError( - "Failed to unassign an instance profile from the current machine" - ) - - token_file = os.environ.get( - "AWS_WEB_IDENTITY_TOKEN_FILE", CONFIG[get_key("iam_web_identity_token_file")] - ) - if os.name == "nt" and token_file.startswith("/tmp"): - token_file = token_file.replace("/tmp", "C:/cygwin/tmp/") - - # Handle the OIDC credentials. - env = dict( - IDP_ISSUER=CONFIG[get_key("iam_web_identity_issuer")], - IDP_JWKS_URI=CONFIG[get_key("iam_web_identity_jwks_uri")], - IDP_RSA_KEY=CONFIG[get_key("iam_web_identity_rsa_key")], - AWS_WEB_IDENTITY_TOKEN_FILE=token_file, - ) - - ret = run(["lib/aws_handle_oidc_creds.py", "token"], env) - if ret != 0: - raise RuntimeWarning("Failed to write the web token") - - # Assume the web role to get temp credentials. - os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file - role_arn = CONFIG[get_key("iam_auth_assume_web_role_name")] - os.environ["AWS_ROLE_ARN"] = role_arn - - creds = _assume_role_with_web_identity(True) - with (HERE / "creds.json").open("w") as fid: - json.dump(creds, fid) - - # Create the user. - token = quote_plus(creds["SessionToken"]) - kwargs = dict( - username=creds["AccessKeyId"], - password=creds["SecretAccessKey"], - authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", - ) - create_user(ASSUMED_WEB_ROLE, kwargs) - - return dict(AWS_WEB_IDENTITY_TOKEN_FILE=token_file, AWS_ROLE_ARN=role_arn) - - -def setup_eks_pod_identity(): - if "PROJECT_DIRECTORY" not in os.environ: - raise ValueError("Please define a PROJECT_DIRECTORY") - - test_path = ( - Path(os.environ["PROJECT_DIRECTORY"]) - / ".evergreen" - / "run-mongodb-aws-eks-test.sh" - ) - if not test_path.exists(): - raise ValueError(f"Please add the file {test_path}!") - - # Set the name for the deployment. - name = f"mongodb-{random.randrange(0, 32767)}" - try: - subprocess.check_call( - ["bash", HERE / "lib" / "eks-pod-setup.sh", name], cwd=HERE / "lib" - ) - finally: - # Tear down the EKS assets. - subprocess.check_call( - ["bash", HERE / "lib" / "eks-pod-teardown.sh", name], cwd=HERE / "lib" - ) - - return dict() +from aws_common import HERE, main def handle_creds(creds: dict): @@ -294,41 +36,5 @@ def handle_creds(creds: dict): fid.write(f'export MONGODB_URI="{MONGODB_URI}"\n') -def main(): - parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") - sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") - - run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") - run_assume_role_cmd.set_defaults(func=setup_assume_role) - - run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") - run_ec2_cmd.set_defaults(func=setup_ec2) - - run_ecs_cmd = sub.add_parser("ecs", help="ECS test") - run_ecs_cmd.set_defaults(func=setup_ecs) - - run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") - run_regular_cmd.set_defaults(func=setup_regular) - - run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") - run_session_creds_cmd.set_defaults(func=setup_session_creds) - - run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") - run_env_creds_cmd.set_defaults(func=setup_env_creds) - - run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") - run_web_identity_cmd.set_defaults(func=setup_web_identity) - - run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") - run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) - - args = parser.parse_args() - func_name = args.func.__name__.replace("setup_", "").replace("_", "-") - LOGGER.info("Running aws_tester.py with %s...", func_name) - creds = args.func() - handle_creds(creds) - LOGGER.info("Running aws_tester.py with %s... done.", func_name) - - if __name__ == "__main__": main() diff --git a/.evergreen/auth_aws/aws_tester_no_uri.py b/.evergreen/auth_aws/aws_tester_no_uri.py new file mode 100755 index 00000000..7a2dcbd6 --- /dev/null +++ b/.evergreen/auth_aws/aws_tester_no_uri.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +Script for testing MONGDOB-AWS authentication without URI credentials. +""" + +from aws_common import HERE, main + + +def handle_creds(creds: dict): + with (HERE / "test-env.sh").open("w", newline="\n") as fid: + fid.write("#!/usr/bin/env bash\n\n") + fid.write("set +x\n") + fid.write( + 'export MONGODB_URI="mongodb://localhost/aws?authMechanism=MONGODB-AWS"\n' + ) + + +if __name__ == "__main__": + main() From 6d409afae2aecab7a11e25d93f0897bdffb24eb3 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 17 Sep 2025 22:42:48 +0200 Subject: [PATCH 02/12] refactor: remove handle_creds from common --- .evergreen/auth_aws/aws_common.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/.evergreen/auth_aws/aws_common.py b/.evergreen/auth_aws/aws_common.py index e7376437..7ceecea5 100644 --- a/.evergreen/auth_aws/aws_common.py +++ b/.evergreen/auth_aws/aws_common.py @@ -265,34 +265,6 @@ def setup_eks_pod_identity(): return dict() -def handle_creds(creds: dict): - if "USER" in creds: - USER = quote_plus(creds["USER"]) - if "PASS" in creds: - PASS = quote_plus(creds["PASS"]) - MONGODB_URI = f"mongodb://{USER}:{PASS}@localhost" - else: - MONGODB_URI = f"mongodb://{USER}@localhost" - elif "MONGODB_URI" in creds: - MONGODB_URI = creds.pop("MONGODB_URI") - else: - MONGODB_URI = "mongodb://localhost" - MONGODB_URI = f"{MONGODB_URI}/aws?authMechanism=MONGODB-AWS" - if "SESSION_TOKEN" in creds: - SESSION_TOKEN = quote_plus(creds["SESSION_TOKEN"]) - MONGODB_URI = ( - f"{MONGODB_URI}&authMechanismProperties=AWS_SESSION_TOKEN:{SESSION_TOKEN}" - ) - with (HERE / "test-env.sh").open("w", newline="\n") as fid: - fid.write("#!/usr/bin/env bash\n\n") - fid.write("set +x\n") - for key, value in creds.items(): - if key in ["USER", "PASS", "SESSION_TOKEN"]: - value = quote_plus(value) # noqa: PLW2901 - fid.write(f"export {key}={value}\n") - fid.write(f'export MONGODB_URI="{MONGODB_URI}"\n') - - def main(): parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") From 769f6476c5a9dae2e7ffb5b2c2b462c2daa439b1 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 17 Sep 2025 23:00:48 +0200 Subject: [PATCH 03/12] refactor: modules --- .evergreen/auth_aws/aws_common.py | 37 ----------------- .evergreen/auth_aws/aws_tester.py | 50 ++++++++++++++++++++++- .evergreen/auth_aws/aws_tester_no_uri.py | 51 +++++++++++++++++++++++- 3 files changed, 99 insertions(+), 39 deletions(-) diff --git a/.evergreen/auth_aws/aws_common.py b/.evergreen/auth_aws/aws_common.py index 7ceecea5..d22c99f2 100644 --- a/.evergreen/auth_aws/aws_common.py +++ b/.evergreen/auth_aws/aws_common.py @@ -2,7 +2,6 @@ Script for testing MONGDOB-AWS authentication. """ -import argparse import json import logging import os @@ -263,39 +262,3 @@ def setup_eks_pod_identity(): ) return dict() - - -def main(): - parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") - sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") - - run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") - run_assume_role_cmd.set_defaults(func=setup_assume_role) - - run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") - run_ec2_cmd.set_defaults(func=setup_ec2) - - run_ecs_cmd = sub.add_parser("ecs", help="ECS test") - run_ecs_cmd.set_defaults(func=setup_ecs) - - run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") - run_regular_cmd.set_defaults(func=setup_regular) - - run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") - run_session_creds_cmd.set_defaults(func=setup_session_creds) - - run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") - run_env_creds_cmd.set_defaults(func=setup_env_creds) - - run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") - run_web_identity_cmd.set_defaults(func=setup_web_identity) - - run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") - run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) - - args = parser.parse_args() - func_name = args.func.__name__.replace("setup_", "").replace("_", "-") - LOGGER.info("Running aws_tester.py with %s...", func_name) - creds = args.func() - handle_creds(creds) - LOGGER.info("Running aws_tester.py with %s... done.", func_name) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index d11ad0d6..41eedae6 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -3,9 +3,21 @@ Script for testing MONGDOB-AWS authentication. """ +import argparse from urllib.parse import quote_plus -from aws_common import HERE, main +from aws_common import ( + HERE, + LOGGER, + setup_assume_role, + setup_eks_pod_identity, + setup_env_creds, + setup_ec2, + setup_ecs, + setup_regular, + setup_session_creds, + setup_web_identity +) def handle_creds(creds: dict): @@ -36,5 +48,41 @@ def handle_creds(creds: dict): fid.write(f'export MONGODB_URI="{MONGODB_URI}"\n') +def main(): + parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") + sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") + + run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") + run_assume_role_cmd.set_defaults(func=setup_assume_role) + + run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") + run_ec2_cmd.set_defaults(func=setup_ec2) + + run_ecs_cmd = sub.add_parser("ecs", help="ECS test") + run_ecs_cmd.set_defaults(func=setup_ecs) + + run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") + run_regular_cmd.set_defaults(func=setup_regular) + + run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") + run_session_creds_cmd.set_defaults(func=setup_session_creds) + + run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") + run_env_creds_cmd.set_defaults(func=setup_env_creds) + + run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") + run_web_identity_cmd.set_defaults(func=setup_web_identity) + + run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") + run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) + + args = parser.parse_args() + func_name = args.func.__name__.replace("setup_", "").replace("_", "-") + LOGGER.info("Running aws_tester.py with %s...", func_name) + creds = args.func() + handle_creds(creds) + LOGGER.info("Running aws_tester.py with %s... done.", func_name) + + if __name__ == "__main__": main() diff --git a/.evergreen/auth_aws/aws_tester_no_uri.py b/.evergreen/auth_aws/aws_tester_no_uri.py index 7a2dcbd6..7e00a3a9 100755 --- a/.evergreen/auth_aws/aws_tester_no_uri.py +++ b/.evergreen/auth_aws/aws_tester_no_uri.py @@ -3,7 +3,20 @@ Script for testing MONGDOB-AWS authentication without URI credentials. """ -from aws_common import HERE, main +import argparse + +from aws_common import ( + HERE, + LOGGER, + setup_assume_role, + setup_eks_pod_identity, + setup_env_creds, + setup_ec2, + setup_ecs, + setup_regular, + setup_session_creds, + setup_web_identity +) def handle_creds(creds: dict): @@ -15,5 +28,41 @@ def handle_creds(creds: dict): ) +def main(): + parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") + sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") + + run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") + run_assume_role_cmd.set_defaults(func=setup_assume_role) + + run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") + run_ec2_cmd.set_defaults(func=setup_ec2) + + run_ecs_cmd = sub.add_parser("ecs", help="ECS test") + run_ecs_cmd.set_defaults(func=setup_ecs) + + run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") + run_regular_cmd.set_defaults(func=setup_regular) + + run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") + run_session_creds_cmd.set_defaults(func=setup_session_creds) + + run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") + run_env_creds_cmd.set_defaults(func=setup_env_creds) + + run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") + run_web_identity_cmd.set_defaults(func=setup_web_identity) + + run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") + run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) + + args = parser.parse_args() + func_name = args.func.__name__.replace("setup_", "").replace("_", "-") + LOGGER.info("Running aws_tester.py with %s...", func_name) + creds = args.func() + handle_creds(creds) + LOGGER.info("Running aws_tester.py with %s... done.", func_name) + + if __name__ == "__main__": main() From 1525ab7ff6e18e3ce05f827cac075bfe525324ee Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 17 Sep 2025 23:03:15 +0200 Subject: [PATCH 04/12] fix: import sorting --- .evergreen/auth_aws/aws_tester.py | 6 +++--- .evergreen/auth_aws/aws_tester_no_uri.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index 41eedae6..4462404c 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -10,13 +10,13 @@ HERE, LOGGER, setup_assume_role, - setup_eks_pod_identity, - setup_env_creds, setup_ec2, setup_ecs, + setup_eks_pod_identity, + setup_env_creds, setup_regular, setup_session_creds, - setup_web_identity + setup_web_identity, ) diff --git a/.evergreen/auth_aws/aws_tester_no_uri.py b/.evergreen/auth_aws/aws_tester_no_uri.py index 7e00a3a9..42f6b0f7 100755 --- a/.evergreen/auth_aws/aws_tester_no_uri.py +++ b/.evergreen/auth_aws/aws_tester_no_uri.py @@ -9,13 +9,13 @@ HERE, LOGGER, setup_assume_role, - setup_eks_pod_identity, - setup_env_creds, setup_ec2, setup_ecs, + setup_eks_pod_identity, + setup_env_creds, setup_regular, setup_session_creds, - setup_web_identity + setup_web_identity, ) From 80ee9d60f78a5cc9bc63116ff725beb896c8bd7f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 26 Sep 2025 20:36:47 +0200 Subject: [PATCH 05/12] refactor: parameterize nouri --- .evergreen/auth_aws/aws_common.py | 264 --------------------- .evergreen/auth_aws/aws_tester.py | 283 +++++++++++++++++++++-- .evergreen/auth_aws/aws_tester_no_uri.py | 68 ------ 3 files changed, 265 insertions(+), 350 deletions(-) delete mode 100644 .evergreen/auth_aws/aws_common.py delete mode 100755 .evergreen/auth_aws/aws_tester_no_uri.py diff --git a/.evergreen/auth_aws/aws_common.py b/.evergreen/auth_aws/aws_common.py deleted file mode 100644 index d22c99f2..00000000 --- a/.evergreen/auth_aws/aws_common.py +++ /dev/null @@ -1,264 +0,0 @@ -""" -Script for testing MONGDOB-AWS authentication. -""" - -import json -import logging -import os -import random -import subprocess -import sys -from functools import partial -from pathlib import Path -from urllib.parse import quote_plus - -from pymongo import MongoClient -from pymongo.errors import OperationFailure - -HERE = Path(__file__).absolute().parent -LOGGER = logging.getLogger(__name__) -logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") - - -def join(*parts): - return os.path.join(*parts).replace(os.sep, "/") - - -sys.path.insert(0, str(HERE / "lib")) -from util import get_key as _get_key - -ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*" -ASSUMED_WEB_ROLE = "arn:aws:sts::857654397073:assumed-role/webIdentityTestRole/*" - -# This varies based on hosting EC2 as the account id and role name can vary -AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/evergreen_task_hosts_instance_role_production/*" -_USE_AWS_SECRETS = False - -try: - with (HERE / "aws_e2e_setup.json").open() as fid: - CONFIG = json.load(fid) - get_key = partial(_get_key, uppercase=False) -except FileNotFoundError: - CONFIG = os.environ.copy() - get_key = partial(_get_key, uppercase=True) - - -def run(args, env): - """Run a python command in a subprocess.""" - env.update(os.environ.copy()) - return subprocess.run([sys.executable, *args], env=env, check=False).returncode - - -def create_user(user, kwargs): - """Create a user and verify access.""" - LOGGER.info("Creating user %s", user) - client = MongoClient(username="bob", password="pwd123") - db = client["$external"] - try: - db.command(dict(createUser=user, roles=[{"role": "read", "db": "aws"}])) - except OperationFailure as e: - if "already exists" not in e.details["errmsg"]: - raise - client.close() - - # Verify access. - client = MongoClient(authMechanism="MONGODB-AWS", **kwargs) - client.aws.command("count", "test") - client.close() - - -def setup_assume_role(): - from aws_assume_role import _assume_role - - # Assume the role to get temp creds. - os.environ["AWS_ACCESS_KEY_ID"] = CONFIG[get_key("iam_auth_assume_aws_account")] - os.environ["AWS_SECRET_ACCESS_KEY"] = CONFIG[ - get_key("iam_auth_assume_aws_secret_access_key") - ] - - role_name = CONFIG[get_key("iam_auth_assume_role_name")] - creds = _assume_role(role_name, quiet=True) - with (HERE / "creds.json").open("w") as fid: - json.dump(creds, fid) - - # Create the user. - token = quote_plus(creds["SessionToken"]) - kwargs = dict( - username=creds["AccessKeyId"], - password=creds["SecretAccessKey"], - authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", - ) - create_user(ASSUMED_ROLE, kwargs) - return dict( - USER=kwargs["username"], - PASS=kwargs["password"], - SESSION_TOKEN=creds["SessionToken"], - ) - - -def setup_ec2(): - # Create the user. - from aws_assign_instance_profile import _assign_instance_policy - - _assign_instance_policy() - os.environ.pop("AWS_ACCESS_KEY_ID", None) - os.environ.pop("AWS_SECRET_ACCESS_KEY", None) - create_user(AWS_ACCOUNT_ARN, dict()) - return dict() - - -def setup_ecs(): - # Set up commands. - mongo_binaries = os.environ["MONGODB_BINARIES"] - project_dir = os.environ["PROJECT_DIRECTORY"] - base_command = f"{sys.executable} -u lib/container_tester.py" - run_prune_command = f"{base_command} -v remote_gc_services --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]}" - - # Get the appropriate task definition based on the version of Ubuntu. - with open("/etc/lsb-release") as fid: - text = fid.read() - if "jammy" in text: - task_definition = CONFIG.get( - get_key("iam_auth_ecs_task_definition_jammy"), None - ) - if task_definition is None: - raise ValueError('Please set "iam_auth_ecs_task_definition_jammy" variable') - elif "focal" in text: - task_definition = CONFIG.get( - get_key("iam_auth_ecs_task_definition_focal"), None - ) - # Fall back to previous task definition for backward compat. - if task_definition is None: - task_definition = CONFIG[get_key("iam_auth_ecs_task_definition")] - else: - raise ValueError("Unsupported ubuntu release") - run_test_command = f"{base_command} -d -v run_e2e_test --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]} --task_definition {task_definition} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_a')]} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_b')]} --security_group {CONFIG[get_key('iam_auth_ecs_security_group')]} --files {mongo_binaries}/mongod:/root/mongod {mongo_binaries}/mongosh:/root/mongosh lib/ecs_hosted_test.js:/root/ecs_hosted_test.js {project_dir}:/root --script lib/ecs_hosted_test.sh" - - # Pass in the AWS credentials as environment variables - # AWS_SHARED_CREDENTIALS_FILE does not work in evergreen for an unknown - # reason - env = dict( - AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ecs_account")], - AWS_SECRET_ACCESS_KEY=CONFIG[get_key("iam_auth_ecs_secret_access_key")], - ) - - # Prune other containers - subprocess.check_call(["/bin/sh", "-c", run_prune_command], env=env) - - # Run the test in a container - subprocess.check_call(["/bin/sh", "-c", run_test_command], env=env) - - return dict() - - -def setup_session_creds(): - # Set up the assume role user, and export the aws vars. - creds = setup_assume_role() - return dict( - AWS_ACCESS_KEY_ID=creds["USER"], - AWS_SECRET_ACCESS_KEY=creds["PASS"], - AWS_SESSION_TOKEN=creds["SESSION_TOKEN"], - ) - - -def setup_regular(): - # Create the user. - kwargs = dict( - username=CONFIG[get_key("iam_auth_ecs_account")], - password=CONFIG[get_key("iam_auth_ecs_secret_access_key")], - ) - create_user(CONFIG[get_key("iam_auth_ecs_account_arn")], kwargs) - - return dict(USER=kwargs["username"], PASS=kwargs["password"]) - - -def setup_env_creds(): - # Set up the regular user, but export the creds as environment vars. - creds = setup_regular() - return dict(AWS_ACCESS_KEY_ID=creds["USER"], AWS_SECRET_ACCESS_KEY=creds["PASS"]) - - -def setup_web_identity(): - from aws_assume_web_role import _assume_role_with_web_identity - - # Unassign the instance profile. - env = dict( - AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ec2_instance_account")], - AWS_SECRET_ACCESS_KEY=CONFIG[ - get_key("iam_auth_ec2_instance_secret_access_key") - ], - ) - ret = run(["lib/aws_unassign_instance_profile.py"], env) - if ret == 2: - raise RuntimeError("Request limit exceeded for AWS API") - - if ret != 0: - LOGGER.debug("return code was %s", ret) - raise RuntimeError( - "Failed to unassign an instance profile from the current machine" - ) - - token_file = os.environ.get( - "AWS_WEB_IDENTITY_TOKEN_FILE", CONFIG[get_key("iam_web_identity_token_file")] - ) - if os.name == "nt" and token_file.startswith("/tmp"): - token_file = token_file.replace("/tmp", "C:/cygwin/tmp/") - - # Handle the OIDC credentials. - env = dict( - IDP_ISSUER=CONFIG[get_key("iam_web_identity_issuer")], - IDP_JWKS_URI=CONFIG[get_key("iam_web_identity_jwks_uri")], - IDP_RSA_KEY=CONFIG[get_key("iam_web_identity_rsa_key")], - AWS_WEB_IDENTITY_TOKEN_FILE=token_file, - ) - - ret = run(["lib/aws_handle_oidc_creds.py", "token"], env) - if ret != 0: - raise RuntimeWarning("Failed to write the web token") - - # Assume the web role to get temp credentials. - os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file - role_arn = CONFIG[get_key("iam_auth_assume_web_role_name")] - os.environ["AWS_ROLE_ARN"] = role_arn - - creds = _assume_role_with_web_identity(True) - with (HERE / "creds.json").open("w") as fid: - json.dump(creds, fid) - - # Create the user. - token = quote_plus(creds["SessionToken"]) - kwargs = dict( - username=creds["AccessKeyId"], - password=creds["SecretAccessKey"], - authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", - ) - create_user(ASSUMED_WEB_ROLE, kwargs) - - return dict(AWS_WEB_IDENTITY_TOKEN_FILE=token_file, AWS_ROLE_ARN=role_arn) - - -def setup_eks_pod_identity(): - if "PROJECT_DIRECTORY" not in os.environ: - raise ValueError("Please define a PROJECT_DIRECTORY") - - test_path = ( - Path(os.environ["PROJECT_DIRECTORY"]) - / ".evergreen" - / "run-mongodb-aws-eks-test.sh" - ) - if not test_path.exists(): - raise ValueError(f"Please add the file {test_path}!") - - # Set the name for the deployment. - name = f"mongodb-{random.randrange(0, 32767)}" - try: - subprocess.check_call( - ["bash", HERE / "lib" / "eks-pod-setup.sh", name], cwd=HERE / "lib" - ) - finally: - # Tear down the EKS assets. - subprocess.check_call( - ["bash", HERE / "lib" / "eks-pod-teardown.sh", name], cwd=HERE / "lib" - ) - - return dict() diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index 4462404c..f222b2a0 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -4,24 +4,270 @@ """ import argparse +import json +import logging +import os +import random +import subprocess +import sys +from functools import partial +from pathlib import Path from urllib.parse import quote_plus -from aws_common import ( - HERE, - LOGGER, - setup_assume_role, - setup_ec2, - setup_ecs, - setup_eks_pod_identity, - setup_env_creds, - setup_regular, - setup_session_creds, - setup_web_identity, -) - - -def handle_creds(creds: dict): - if "USER" in creds: +from pymongo import MongoClient +from pymongo.errors import OperationFailure + +HERE = Path(__file__).absolute().parent +LOGGER = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") + + +def join(*parts): + return os.path.join(*parts).replace(os.sep, "/") + + +sys.path.insert(0, str(HERE / "lib")) +from util import get_key as _get_key + +ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*" +ASSUMED_WEB_ROLE = "arn:aws:sts::857654397073:assumed-role/webIdentityTestRole/*" + +# This varies based on hosting EC2 as the account id and role name can vary +AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/evergreen_task_hosts_instance_role_production/*" +_USE_AWS_SECRETS = False + +try: + with (HERE / "aws_e2e_setup.json").open() as fid: + CONFIG = json.load(fid) + get_key = partial(_get_key, uppercase=False) +except FileNotFoundError: + CONFIG = os.environ.copy() + get_key = partial(_get_key, uppercase=True) + + +def run(args, env): + """Run a python command in a subprocess.""" + env.update(os.environ.copy()) + return subprocess.run([sys.executable, *args], env=env, check=False).returncode + + +def create_user(user, kwargs): + """Create a user and verify access.""" + LOGGER.info("Creating user %s", user) + client = MongoClient(username="bob", password="pwd123") + db = client["$external"] + try: + db.command(dict(createUser=user, roles=[{"role": "read", "db": "aws"}])) + except OperationFailure as e: + if "already exists" not in e.details["errmsg"]: + raise + client.close() + + # Verify access. + client = MongoClient(authMechanism="MONGODB-AWS", **kwargs) + client.aws.command("count", "test") + client.close() + + +def setup_assume_role(): + from aws_assume_role import _assume_role + + # Assume the role to get temp creds. + os.environ["AWS_ACCESS_KEY_ID"] = CONFIG[get_key("iam_auth_assume_aws_account")] + os.environ["AWS_SECRET_ACCESS_KEY"] = CONFIG[ + get_key("iam_auth_assume_aws_secret_access_key") + ] + + role_name = CONFIG[get_key("iam_auth_assume_role_name")] + creds = _assume_role(role_name, quiet=True) + with (HERE / "creds.json").open("w") as fid: + json.dump(creds, fid) + + # Create the user. + token = quote_plus(creds["SessionToken"]) + kwargs = dict( + username=creds["AccessKeyId"], + password=creds["SecretAccessKey"], + authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", + ) + create_user(ASSUMED_ROLE, kwargs) + return dict( + USER=kwargs["username"], + PASS=kwargs["password"], + SESSION_TOKEN=creds["SessionToken"], + ) + + +def setup_ec2(): + # Create the user. + from aws_assign_instance_profile import _assign_instance_policy + + _assign_instance_policy() + os.environ.pop("AWS_ACCESS_KEY_ID", None) + os.environ.pop("AWS_SECRET_ACCESS_KEY", None) + create_user(AWS_ACCOUNT_ARN, dict()) + return dict() + + +def setup_ecs(): + # Set up commands. + mongo_binaries = os.environ["MONGODB_BINARIES"] + project_dir = os.environ["PROJECT_DIRECTORY"] + base_command = f"{sys.executable} -u lib/container_tester.py" + run_prune_command = f"{base_command} -v remote_gc_services --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]}" + + # Get the appropriate task definition based on the version of Ubuntu. + with open("/etc/lsb-release") as fid: + text = fid.read() + if "jammy" in text: + task_definition = CONFIG.get( + get_key("iam_auth_ecs_task_definition_jammy"), None + ) + if task_definition is None: + raise ValueError('Please set "iam_auth_ecs_task_definition_jammy" variable') + elif "focal" in text: + task_definition = CONFIG.get( + get_key("iam_auth_ecs_task_definition_focal"), None + ) + # Fall back to previous task definition for backward compat. + if task_definition is None: + task_definition = CONFIG[get_key("iam_auth_ecs_task_definition")] + else: + raise ValueError("Unsupported ubuntu release") + run_test_command = f"{base_command} -d -v run_e2e_test --cluster {CONFIG[get_key('iam_auth_ecs_cluster')]} --task_definition {task_definition} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_a')]} --subnets {CONFIG[get_key('iam_auth_ecs_subnet_b')]} --security_group {CONFIG[get_key('iam_auth_ecs_security_group')]} --files {mongo_binaries}/mongod:/root/mongod {mongo_binaries}/mongosh:/root/mongosh lib/ecs_hosted_test.js:/root/ecs_hosted_test.js {project_dir}:/root --script lib/ecs_hosted_test.sh" + + # Pass in the AWS credentials as environment variables + # AWS_SHARED_CREDENTIALS_FILE does not work in evergreen for an unknown + # reason + env = dict( + AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ecs_account")], + AWS_SECRET_ACCESS_KEY=CONFIG[get_key("iam_auth_ecs_secret_access_key")], + ) + + # Prune other containers + subprocess.check_call(["/bin/sh", "-c", run_prune_command], env=env) + + # Run the test in a container + subprocess.check_call(["/bin/sh", "-c", run_test_command], env=env) + + return dict() + + +def setup_session_creds(): + # Set up the assume role user, and export the aws vars. + creds = setup_assume_role() + return dict( + AWS_ACCESS_KEY_ID=creds["USER"], + AWS_SECRET_ACCESS_KEY=creds["PASS"], + AWS_SESSION_TOKEN=creds["SESSION_TOKEN"], + ) + + +def setup_regular(): + # Create the user. + kwargs = dict( + username=CONFIG[get_key("iam_auth_ecs_account")], + password=CONFIG[get_key("iam_auth_ecs_secret_access_key")], + ) + create_user(CONFIG[get_key("iam_auth_ecs_account_arn")], kwargs) + + return dict(USER=kwargs["username"], PASS=kwargs["password"]) + + +def setup_env_creds(): + # Set up the regular user, but export the creds as environment vars. + creds = setup_regular() + return dict(AWS_ACCESS_KEY_ID=creds["USER"], AWS_SECRET_ACCESS_KEY=creds["PASS"]) + + +def setup_web_identity(): + from aws_assume_web_role import _assume_role_with_web_identity + + # Unassign the instance profile. + env = dict( + AWS_ACCESS_KEY_ID=CONFIG[get_key("iam_auth_ec2_instance_account")], + AWS_SECRET_ACCESS_KEY=CONFIG[ + get_key("iam_auth_ec2_instance_secret_access_key") + ], + ) + ret = run(["lib/aws_unassign_instance_profile.py"], env) + if ret == 2: + raise RuntimeError("Request limit exceeded for AWS API") + + if ret != 0: + LOGGER.debug("return code was %s", ret) + raise RuntimeError( + "Failed to unassign an instance profile from the current machine" + ) + + token_file = os.environ.get( + "AWS_WEB_IDENTITY_TOKEN_FILE", CONFIG[get_key("iam_web_identity_token_file")] + ) + if os.name == "nt" and token_file.startswith("/tmp"): + token_file = token_file.replace("/tmp", "C:/cygwin/tmp/") + + # Handle the OIDC credentials. + env = dict( + IDP_ISSUER=CONFIG[get_key("iam_web_identity_issuer")], + IDP_JWKS_URI=CONFIG[get_key("iam_web_identity_jwks_uri")], + IDP_RSA_KEY=CONFIG[get_key("iam_web_identity_rsa_key")], + AWS_WEB_IDENTITY_TOKEN_FILE=token_file, + ) + + ret = run(["lib/aws_handle_oidc_creds.py", "token"], env) + if ret != 0: + raise RuntimeWarning("Failed to write the web token") + + # Assume the web role to get temp credentials. + os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file + role_arn = CONFIG[get_key("iam_auth_assume_web_role_name")] + os.environ["AWS_ROLE_ARN"] = role_arn + + creds = _assume_role_with_web_identity(True) + with (HERE / "creds.json").open("w") as fid: + json.dump(creds, fid) + + # Create the user. + token = quote_plus(creds["SessionToken"]) + kwargs = dict( + username=creds["AccessKeyId"], + password=creds["SecretAccessKey"], + authmechanismproperties=f"AWS_SESSION_TOKEN:{token}", + ) + create_user(ASSUMED_WEB_ROLE, kwargs) + + return dict(AWS_WEB_IDENTITY_TOKEN_FILE=token_file, AWS_ROLE_ARN=role_arn) + + +def setup_eks_pod_identity(): + if "PROJECT_DIRECTORY" not in os.environ: + raise ValueError("Please define a PROJECT_DIRECTORY") + + test_path = ( + Path(os.environ["PROJECT_DIRECTORY"]) + / ".evergreen" + / "run-mongodb-aws-eks-test.sh" + ) + if not test_path.exists(): + raise ValueError(f"Please add the file {test_path}!") + + # Set the name for the deployment. + name = f"mongodb-{random.randrange(0, 32767)}" + try: + subprocess.check_call( + ["bash", HERE / "lib" / "eks-pod-setup.sh", name], cwd=HERE / "lib" + ) + finally: + # Tear down the EKS assets. + subprocess.check_call( + ["bash", HERE / "lib" / "eks-pod-teardown.sh", name], cwd=HERE / "lib" + ) + + return dict() + + +def handle_creds(creds: dict, nouri: bool): + if "USER" in creds and not nouri: USER = quote_plus(creds["USER"]) if "PASS" in creds: PASS = quote_plus(creds["PASS"]) @@ -33,7 +279,7 @@ def handle_creds(creds: dict): else: MONGODB_URI = "mongodb://localhost" MONGODB_URI = f"{MONGODB_URI}/aws?authMechanism=MONGODB-AWS" - if "SESSION_TOKEN" in creds: + if "SESSION_TOKEN" in creds and not nouri: SESSION_TOKEN = quote_plus(creds["SESSION_TOKEN"]) MONGODB_URI = ( f"{MONGODB_URI}&authMechanismProperties=AWS_SESSION_TOKEN:{SESSION_TOKEN}" @@ -50,6 +296,7 @@ def handle_creds(creds: dict): def main(): parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") + parser.add_argument("--nouri", action="store_true", default=False) sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") @@ -80,7 +327,7 @@ def main(): func_name = args.func.__name__.replace("setup_", "").replace("_", "-") LOGGER.info("Running aws_tester.py with %s...", func_name) creds = args.func() - handle_creds(creds) + handle_creds(creds, args.no_uri) LOGGER.info("Running aws_tester.py with %s... done.", func_name) diff --git a/.evergreen/auth_aws/aws_tester_no_uri.py b/.evergreen/auth_aws/aws_tester_no_uri.py deleted file mode 100755 index 42f6b0f7..00000000 --- a/.evergreen/auth_aws/aws_tester_no_uri.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -""" -Script for testing MONGDOB-AWS authentication without URI credentials. -""" - -import argparse - -from aws_common import ( - HERE, - LOGGER, - setup_assume_role, - setup_ec2, - setup_ecs, - setup_eks_pod_identity, - setup_env_creds, - setup_regular, - setup_session_creds, - setup_web_identity, -) - - -def handle_creds(creds: dict): - with (HERE / "test-env.sh").open("w", newline="\n") as fid: - fid.write("#!/usr/bin/env bash\n\n") - fid.write("set +x\n") - fid.write( - 'export MONGODB_URI="mongodb://localhost/aws?authMechanism=MONGODB-AWS"\n' - ) - - -def main(): - parser = argparse.ArgumentParser(description="MONGODB-AWS tester.") - sub = parser.add_subparsers(title="Tester subcommands", help="sub-command help") - - run_assume_role_cmd = sub.add_parser("assume-role", help="Assume role test") - run_assume_role_cmd.set_defaults(func=setup_assume_role) - - run_ec2_cmd = sub.add_parser("ec2", help="EC2 test") - run_ec2_cmd.set_defaults(func=setup_ec2) - - run_ecs_cmd = sub.add_parser("ecs", help="ECS test") - run_ecs_cmd.set_defaults(func=setup_ecs) - - run_regular_cmd = sub.add_parser("regular", help="Regular credentials test") - run_regular_cmd.set_defaults(func=setup_regular) - - run_session_creds_cmd = sub.add_parser("session-creds", help="Session credentials") - run_session_creds_cmd.set_defaults(func=setup_session_creds) - - run_env_creds_cmd = sub.add_parser("env-creds", help="Environment credentials") - run_env_creds_cmd.set_defaults(func=setup_env_creds) - - run_web_identity_cmd = sub.add_parser("web-identity", help="Web identity test") - run_web_identity_cmd.set_defaults(func=setup_web_identity) - - run_eks_pod_identity_cmd = sub.add_parser("eks", help="EKS pod identity test") - run_eks_pod_identity_cmd.set_defaults(func=setup_eks_pod_identity) - - args = parser.parse_args() - func_name = args.func.__name__.replace("setup_", "").replace("_", "-") - LOGGER.info("Running aws_tester.py with %s...", func_name) - creds = args.func() - handle_creds(creds) - LOGGER.info("Running aws_tester.py with %s... done.", func_name) - - -if __name__ == "__main__": - main() From 0b22a70bd5ff7bd7defe2b1d565923fe4e20e0bf Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Fri, 26 Sep 2025 20:48:49 +0200 Subject: [PATCH 06/12] chore: fix typo --- .evergreen/auth_aws/aws_tester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index f222b2a0..6ffb5ab9 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -327,7 +327,7 @@ def main(): func_name = args.func.__name__.replace("setup_", "").replace("_", "-") LOGGER.info("Running aws_tester.py with %s...", func_name) creds = args.func() - handle_creds(creds, args.no_uri) + handle_creds(creds, args.nouri) LOGGER.info("Running aws_tester.py with %s... done.", func_name) From 80b82739c200281b1f3ed0b7d8b97b76ee23de24 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 14:01:05 +0200 Subject: [PATCH 07/12] test: adding nouri tests --- .evergreen/tests/test-aws.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index b522ddae..4064dcd5 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -19,6 +19,17 @@ cat test-env.sh | grep -q SESSION_TOKEN cat test-env.sh | grep MONGODB_URI | grep -q "@" rm test-env.sh +bash aws_setup.sh assume-role --nouri +cat test-env.sh | grep -q USER +cat test-env.sh | grep -q PASS +cat test-env.sh | grep -q SESSION_TOKEN +cat test-env.sh | grep -q AWS_ACCESS_KEY_ID +cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY +cat test-env.sh | grep -q AWS_SESSION_TOKEN +# Ensure there is no password in the URI. +cat test-env.sh | grep MONGODB_URI | grep -v -q "@" +rm test-env.sh + bash aws_setup.sh ec2 # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" @@ -31,6 +42,17 @@ cat test-env.sh | grep -v -q SESSION_TOKEN cat test-env.sh | grep MONGODB_URI | grep -q "@" rm test-env.sh +bash aws_setup.sh regular --nouri +cat test-env.sh | grep -q USER +cat test-env.sh | grep -q PASS +cat test-env.sh | grep -q SESSION_TOKEN +cat test-env.sh | grep -q AWS_ACCESS_KEY_ID +cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY +cat test-env.sh | grep -q AWS_SESSION_TOKEN +# Ensure there is no password in the URI. +cat test-env.sh | grep MONGODB_URI | grep -v -q "@" +rm test-env.sh + bash aws_setup.sh session-creds cat test-env.sh | grep -q AWS_ACCESS_KEY_ID cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY From 76cda5581c35b07ae9167f87d7bc1e63ca587244 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 14:10:39 +0200 Subject: [PATCH 08/12] test: arg position --- .evergreen/tests/test-aws.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index 4064dcd5..09887883 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -19,7 +19,7 @@ cat test-env.sh | grep -q SESSION_TOKEN cat test-env.sh | grep MONGODB_URI | grep -q "@" rm test-env.sh -bash aws_setup.sh assume-role --nouri +bash aws_setup.sh --nouri assume-role cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -q SESSION_TOKEN @@ -42,7 +42,7 @@ cat test-env.sh | grep -v -q SESSION_TOKEN cat test-env.sh | grep MONGODB_URI | grep -q "@" rm test-env.sh -bash aws_setup.sh regular --nouri +bash aws_setup.sh --nouri regular cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -q SESSION_TOKEN From 80fb27810fb8b291695df8e296b1aa2d701ab63a Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 14:45:09 +0200 Subject: [PATCH 09/12] test: fix --- .evergreen/tests/test-aws.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index 09887883..0a505029 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -23,9 +23,6 @@ bash aws_setup.sh --nouri assume-role cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -q SESSION_TOKEN -cat test-env.sh | grep -q AWS_ACCESS_KEY_ID -cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY -cat test-env.sh | grep -q AWS_SESSION_TOKEN # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" rm test-env.sh @@ -46,9 +43,6 @@ bash aws_setup.sh --nouri regular cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -q SESSION_TOKEN -cat test-env.sh | grep -q AWS_ACCESS_KEY_ID -cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY -cat test-env.sh | grep -q AWS_SESSION_TOKEN # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" rm test-env.sh From 78cabb250c7ee5a1a22e323cda86a5d2706ba1c1 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 14:57:40 +0200 Subject: [PATCH 10/12] test: fix --- .evergreen/tests/test-aws.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index 0a505029..6dfb4cc5 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -42,7 +42,7 @@ rm test-env.sh bash aws_setup.sh --nouri regular cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS -cat test-env.sh | grep -q SESSION_TOKEN +cat test-env.sh | grep -v -q SESSION_TOKEN # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" rm test-env.sh From 89830b5b10677aa78a9e40fc1e869ffb86cf7162 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 16:07:07 +0200 Subject: [PATCH 11/12] test: add to env with assume role --- .evergreen/auth_aws/aws_tester.py | 3 +++ .evergreen/tests/test-aws.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index 6ffb5ab9..455c69c3 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -95,6 +95,9 @@ def setup_assume_role(): USER=kwargs["username"], PASS=kwargs["password"], SESSION_TOKEN=creds["SessionToken"], + AWS_ACCESS_KEY_ID=kwargs["username"], + AWS_SECRET_ACCESS_KEY=kwargs["password"], + AWS_SESSION_TOKEN=creds["SessionToken"], ) diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index 6dfb4cc5..4403a945 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -23,6 +23,9 @@ bash aws_setup.sh --nouri assume-role cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -q SESSION_TOKEN +cat test-env.sh | grep -q AWS_ACCESS_KEY_ID +cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY +cat test-env.sh | grep -q AWS_SESSION_TOKEN # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" rm test-env.sh From 5fa39709dde92f78b3d3b7364757c3a5336096bb Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 30 Sep 2025 16:13:49 +0200 Subject: [PATCH 12/12] test: regular --- .evergreen/auth_aws/aws_tester.py | 7 ++++++- .evergreen/tests/test-aws.sh | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.evergreen/auth_aws/aws_tester.py b/.evergreen/auth_aws/aws_tester.py index 455c69c3..97ba1dde 100755 --- a/.evergreen/auth_aws/aws_tester.py +++ b/.evergreen/auth_aws/aws_tester.py @@ -174,7 +174,12 @@ def setup_regular(): ) create_user(CONFIG[get_key("iam_auth_ecs_account_arn")], kwargs) - return dict(USER=kwargs["username"], PASS=kwargs["password"]) + return dict( + USER=kwargs["username"], + PASS=kwargs["password"], + AWS_ACCESS_KEY_ID=kwargs["username"], + AWS_SECRET_ACCESS_KEY=kwargs["password"], + ) def setup_env_creds(): diff --git a/.evergreen/tests/test-aws.sh b/.evergreen/tests/test-aws.sh index 4403a945..986475b4 100755 --- a/.evergreen/tests/test-aws.sh +++ b/.evergreen/tests/test-aws.sh @@ -46,6 +46,9 @@ bash aws_setup.sh --nouri regular cat test-env.sh | grep -q USER cat test-env.sh | grep -q PASS cat test-env.sh | grep -v -q SESSION_TOKEN +cat test-env.sh | grep -q AWS_ACCESS_KEY_ID +cat test-env.sh | grep -q AWS_SECRET_ACCESS_KEY +cat test-env.sh | grep -v -q AWS_SESSION_TOKEN # Ensure there is no password in the URI. cat test-env.sh | grep MONGODB_URI | grep -v -q "@" rm test-env.sh