From 9d9aee79d3f41b8ba21158caa165820143c2e769 Mon Sep 17 00:00:00 2001 From: jem Date: Fri, 6 Mar 2020 16:10:18 +0100 Subject: [PATCH] separate mfa from key-only aws --- build.py | 2 +- src/main/python/ddadevops/__init__.py | 1 + src/main/python/ddadevops/aws_mfa_mixin.py | 82 +++++++++++++++++++ src/main/python/ddadevops/aws_mixin.py | 71 +--------------- src/main/python/ddadevops/devops_build.py | 4 +- .../ddadevops/devops_terraform_build.py | 5 +- src/main/python/ddadevops/hetzner_mixin.py | 4 +- 7 files changed, 95 insertions(+), 74 deletions(-) create mode 100644 src/main/python/ddadevops/aws_mfa_mixin.py diff --git a/build.py b/build.py index 82b8082..3646a8a 100644 --- a/build.py +++ b/build.py @@ -27,7 +27,7 @@ use_plugin("python.distutils") default_task = "publish" name = "ddadevops" -version = "0.4.1.dev0" +version = "0.4.1.dev1" summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud" description = __doc__ authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")] diff --git a/src/main/python/ddadevops/__init__.py b/src/main/python/ddadevops/__init__.py index dc20688..ef6e1d0 100644 --- a/src/main/python/ddadevops/__init__.py +++ b/src/main/python/ddadevops/__init__.py @@ -9,6 +9,7 @@ from .devops_build import DevopsBuild, create_devops_build_config from .devops_terraform_build import DevopsTerraformBuild, create_devops_terraform_build_config from .hetzner_mixin import HetznerMixin, add_hetzner_mixin_config from .aws_mixin import AwsMixin, add_aws_mixin_config +from .aws_mfa_mixin import AwsMfaMixin, add_aws_mfa_mixin_config from .dda_pallet_mixin import DdaPalletMixin, add_dda_pallet_mixin_config __version__ = "${version}" \ No newline at end of file diff --git a/src/main/python/ddadevops/aws_mfa_mixin.py b/src/main/python/ddadevops/aws_mfa_mixin.py new file mode 100644 index 0000000..a46bfc7 --- /dev/null +++ b/src/main/python/ddadevops/aws_mfa_mixin.py @@ -0,0 +1,82 @@ +from python_terraform import * +from boto3 import * +from .credential import gopass_credential_from_env_path +from .python_util import execute +from .aws_mixin import AwsMixin + + +def add_aws_mfa_mixin_config(config, account_id, region, +mfa_role='developer', mfa_account_prefix='', mfa_login_account_suffix='main'): + config.update({'AwsMfaMixin': + {'account_id': account_id, + 'region': region, + 'mfa_role': mfa_role, + 'mfa_account_prefix': mfa_account_prefix, + 'mfa_login_account_suffix': mfa_login_account_suffix}}) + return config + + +class AwsMfaMixin(AwsMixin): + + def __init__(self, project, config): + super().__init__(project, config) + project.build_depends_on('boto3') + project.build_depends_on('mfa') + aws_mfa_mixin_config = config['AwsMfaMixin'] + self.account_id = aws_mfa_mixin_config['account_id'] + self.region = aws_mfa_mixin_config['region'] + self.mfa_role = aws_mfa_mixin_config['mfa_role'] + self.mfa_account_prefix = aws_mfa_mixin_config['mfa_account_prefix'] + self.mfa_login_account_suffix = aws_mfa_mixin_config['mfa_login_account_suffix'] + + def project_vars(self): + ret = super().project_vars() + ret.update({'account_name': self.account_name, + 'account_id': self.account_id, + 'region': self.region, + 'mfa_role': self.mfa_role, + 'mfa_account_prefix': self.mfa_account_prefix, + 'mfa_login_account_suffix': self.mfa_login_account_suffix}) + return ret + + def get_username_from_account(self, p_account_name): + login_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name + + '\]" | grep username | awk -F= \'{print $2}\'', shell=True) + return login_id + + def get_account_id_from_account(self, p_account_name): + account_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name + + '\]" | grep account | awk -F= \'{print $2}\'', shell=True) + return account_id + + def get_mfa(self, mfa_path='aws'): + mfa_token = execute('mfa otp ' + mfa_path, shell=True) + return mfa_token + + def write_aws_config(self, to_profile, key, secret): + execute('aws configure --profile ' + to_profile + + ' set ' + key + ' ' + secret, shell=True) + + def get_mfa_session(self, toke=None): + from_account_name = self.mfa_account_prefix + self.mfa_login_account_suffix + from_account_id = self.get_account_id_from_account(from_account_name) + to_account_name = self.mfa_account_prefix + self.account_name + to_account_id = self.get_account_id_from_account(to_account_name) + login_id = self.get_username_from_account(from_account_name) + mfa_token = self.get_mfa() + ses = Session(profile_name=from_account_name) + sts_client = ses.client('sts') + response = sts_client.assume_role( + RoleArn='arn:aws:iam::' + to_account_id + ':role/' + self.mfa_role, + RoleSessionName=to_account_id + '-' + self.account_name + '-' + self.mfa_role, + SerialNumber='arn:aws:iam::' + from_account_id + ':mfa/' + login_id, + TokenCode=mfa_token + ) + + self.write_aws_config(to_account_name, 'aws_access_key_id', + response['Credentials']['AccessKeyId']) + self.write_aws_config(to_account_name, 'aws_secret_access_key', + response['Credentials']['SecretAccessKey']) + self.write_aws_config(to_account_name, 'aws_session_token', + response['Credentials']['SessionToken']) + print('got token') diff --git a/src/main/python/ddadevops/aws_mixin.py b/src/main/python/ddadevops/aws_mixin.py index bdc9c80..d6021eb 100644 --- a/src/main/python/ddadevops/aws_mixin.py +++ b/src/main/python/ddadevops/aws_mixin.py @@ -1,19 +1,10 @@ from python_terraform import * -from boto3 import * -from .credential import gopass_credential_from_env_path -from .python_util import execute from .devops_terraform_build import DevopsTerraformBuild -def add_aws_mixin_config(config, account_name, account_id, region, -mfa_role='developer', mfa_account_prefix='', mfa_login_account_suffix='main'): +def add_aws_mixin_config(config, account_name): config.update({'AwsMixin': - {'account_name': account_name, - 'account_id': account_id, - 'region': region, - 'mfa_role': mfa_role, - 'mfa_account_prefix': mfa_account_prefix, - 'mfa_login_account_suffix': mfa_login_account_suffix}}) + {'account_name': account_name}}) return config @@ -21,27 +12,15 @@ class AwsMixin(DevopsTerraformBuild): def __init__(self, project, config): super().__init__(project, config) - project.build_depends_on('boto3') - project.build_depends_on('mfa') aws_mixin_config = config['AwsMixin'] self.account_name = aws_mixin_config['account_name'] - self.account_id = aws_mixin_config['account_id'] - self.region = aws_mixin_config['region'] - self.mfa_role = aws_mixin_config['mfa_role'] - self.mfa_account_prefix = aws_mixin_config['mfa_account_prefix'] - self.mfa_login_account_suffix = aws_mixin_config['mfa_login_account_suffix'] - + def backend_config(self): return "backend." + self.account_name + "." + self.stage + ".properties" def project_vars(self): ret = super().project_vars() - ret.update({'account_name': self.account_name, - 'account_id': self.account_id, - 'region': self.region, - 'mfa_role': self.mfa_role, - 'mfa_account_prefix': self.mfa_account_prefix, - 'mfa_login_account_suffix': self.mfa_login_account_suffix}) + ret.update({'account_name': self.account_name}) return ret def init_client(self): @@ -69,45 +48,3 @@ class AwsMixin(DevopsTerraformBuild): tf = self.init_client() tf.destroy(capture_output=False, auto_approve=p_auto_approve, var=self.project_vars(), var_file=self.backend_config()) - - def get_username_from_account(self, p_account_name): - login_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name + - '\]" | grep username | awk -F= \'{print $2}\'', shell=True) - return login_id - - def get_account_id_from_account(self, p_account_name): - account_id = execute('cat ~/.aws/accounts | grep -A 2 "\[' + p_account_name + - '\]" | grep account | awk -F= \'{print $2}\'', shell=True) - return account_id - - def get_mfa(self, mfa_path='aws'): - mfa_token = execute('mfa otp ' + mfa_path, shell=True) - return mfa_token - - def write_aws_config(self, to_profile, key, secret): - execute('aws configure --profile ' + to_profile + - ' set ' + key + ' ' + secret, shell=True) - - def get_mfa_session(self, toke=None): - from_account_name = self.mfa_account_prefix + self.mfa_login_account_suffix - from_account_id = self.get_account_id_from_account(from_account_name) - to_account_name = self.mfa_account_prefix + self.account_name - to_account_id = self.get_account_id_from_account(to_account_name) - login_id = self.get_username_from_account(from_account_name) - mfa_token = self.get_mfa() - ses = Session(profile_name=from_account_name) - sts_client = ses.client('sts') - response = sts_client.assume_role( - RoleArn='arn:aws:iam::' + to_account_id + ':role/' + self.mfa_role, - RoleSessionName=to_account_id + '-' + self.account_name + '-' + self.mfa_role, - SerialNumber='arn:aws:iam::' + from_account_id + ':mfa/' + login_id, - TokenCode=mfa_token - ) - - self.write_aws_config(to_account_name, 'aws_access_key_id', - response['Credentials']['AccessKeyId']) - self.write_aws_config(to_account_name, 'aws_secret_access_key', - response['Credentials']['SecretAccessKey']) - self.write_aws_config(to_account_name, 'aws_session_token', - response['Credentials']['SessionToken']) - print('got token') diff --git a/src/main/python/ddadevops/devops_build.py b/src/main/python/ddadevops/devops_build.py index f4e2797..f2f2893 100644 --- a/src/main/python/ddadevops/devops_build.py +++ b/src/main/python/ddadevops/devops_build.py @@ -2,13 +2,15 @@ from subprocess import run from .python_util import filter_none -def create_devops_build_config(stage, project_root_path, build_commons_path, module, build_dir_name='target'): +def create_devops_build_config(stage, project_root_path, build_commons_path, module, + build_dir_name='target'): return {'stage': stage, 'project_root_path': project_root_path, 'build_commons_path': build_commons_path, 'module': module, 'build_dir_name': build_dir_name} + class DevopsBuild: def __init__(self, project, config): diff --git a/src/main/python/ddadevops/devops_terraform_build.py b/src/main/python/ddadevops/devops_terraform_build.py index 46bd5ce..75f4c14 100644 --- a/src/main/python/ddadevops/devops_terraform_build.py +++ b/src/main/python/ddadevops/devops_terraform_build.py @@ -7,15 +7,14 @@ from .devops_build import DevopsBuild, create_devops_build_config def create_devops_terraform_build_config(stage, project_root_path, build_commons_path, module, - account_name, additional_vars, + additional_vars, build_dir_name='target', terraform_build_commons_dir_name='terraform', output_json_name='output.json', use_workspace=True): ret = create_devops_build_config( stage, project_root_path, build_commons_path, module, build_dir_name) - ret.update({'account_name': account_name, - 'additional_vars': additional_vars, + ret.update({'additional_vars': additional_vars, 'terraform_build_commons_dir_name': terraform_build_commons_dir_name, 'output_json_name': output_json_name, 'use_workspace': use_workspace}) diff --git a/src/main/python/ddadevops/hetzner_mixin.py b/src/main/python/ddadevops/hetzner_mixin.py index d7d43c3..77b9cea 100644 --- a/src/main/python/ddadevops/hetzner_mixin.py +++ b/src/main/python/ddadevops/hetzner_mixin.py @@ -2,9 +2,9 @@ from .credential import gopass_credential_from_env_path from .devops_terraform_build import DevopsTerraformBuild -def add_hetzner_mixin_config(config): +def add_hetzner_mixin_config(config, hetzner_api_key_path_env='HETZNER_API_KEY_PATH'): config.update({'HetznerMixin': - {'HETZNER_API_KEY_PATH_ENVIRONMENT': 'HETZNER_API_KEY_PATH'}}) + {'HETZNER_API_KEY_PATH_ENVIRONMENT': hetzner_api_key_path_env}}) return config