Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kms explosion fix #882

Merged
merged 65 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
ad4ab1f
Add Additional Error Messages for KMS Key lookup on imported dataset …
noah-paige Sep 15, 2023
dbbef3c
Get Latest in main to v2m1m0 (#771)
noah-paige Sep 19, 2023
d096160
Handle Environment Import of IAM service roles (#749)
noah-paige Sep 26, 2023
a53434f
Build Compliant Names for Opensearch Resources (#750)
noah-paige Oct 5, 2023
16c7026
Merge branch 'main' into v2m1m0
dlpzx Oct 10, 2023
c61ba15
Update Lambda runtime (#782)
nikpodsh Oct 10, 2023
f84250e
Feat: limit pivot role S3 permissions (#780)
dlpzx Oct 12, 2023
7d9122d
Fix: ensure valid environments for share request and other objects cr…
dlpzx Oct 12, 2023
1801cf1
Adding configurable session timeout to IDP (#786)
manjulaK Oct 13, 2023
599fc1a
Fix: shell true semgrep (#760)
dlpzx Oct 16, 2023
b356bf2
Fix: allow to submit a share when you are both and approver and a req…
fourtyplustwo Oct 16, 2023
793a078
feat: redirect upon creating a share request (#799)
fourtyplustwo Oct 16, 2023
f448613
Fix: condition when there are no public subnets (#794)
lorchda Oct 18, 2023
66b9a08
feat: removing unused variable (#815)
fourtyplustwo Oct 18, 2023
c833c26
feat: Handle Pre-filtering of tables (#811)
anushka-singh Oct 18, 2023
6cc564e
Fix Check other share exists before clean up (#769)
noah-paige Oct 18, 2023
8b7b82e
Email Notification on Share Workflow - Issue - 734 (#818)
TejasRGitHub Oct 20, 2023
48c32e5
feat: adding frontend and backend feature flags (#817)
fourtyplustwo Oct 25, 2023
6d727e9
Feat: Refactor notifications from core to modules (#822)
dlpzx Oct 26, 2023
8ad760b
Merge branch 'main' into v2m1m0
dlpzx Oct 27, 2023
3f100b4
Feat: pivot role limit kms (#830)
dlpzx Oct 27, 2023
fb7b61b
Make hosted_zone_id optional, code update (#812)
lorchda Oct 27, 2023
b51da2c
Clean-up for v2.1 (#843)
dlpzx Oct 30, 2023
6d3c016
Merge branch 'main' into v2m1m0
dlpzx Oct 27, 2023
7912a24
Feat: pivot role limit kms (#830)
dlpzx Oct 27, 2023
55c579b
Make hosted_zone_id optional, code update (#812)
lorchda Oct 27, 2023
92d4324
Clean-up for v2.1 (#843)
dlpzx Oct 30, 2023
5fb7cf8
feat: Enabling S3 bucket share
anushka-singh Oct 31, 2023
cf9afc1
feat: Enabling S3 bucket share
anushka-singh Oct 31, 2023
ddf8623
Merge branch 'v2m1m0' of https://github.com/anushka-singh/aws-dataall…
anushka-singh Oct 31, 2023
b54860d
fix: adding missing pivot role permission to get key policy (#845)
fourtyplustwo Oct 31, 2023
a05e548
Merge branch 'v2m1m0' into anu-s3-copy
dlpzx Oct 31, 2023
1365e92
Revert overwrites 2.
dlpzx Oct 31, 2023
bbcfbd5
Revert overwrites 3.
dlpzx Oct 31, 2023
9e8cdf1
Revert overwrites 4.
dlpzx Oct 31, 2023
5d90797
Revert overwrites 4.
dlpzx Oct 31, 2023
94be491
Revert overwrites 5.
dlpzx Oct 31, 2023
cff577f
Revert overwrites 6.
dlpzx Oct 31, 2023
5ff80fb
Revert overwrites 7.
dlpzx Oct 31, 2023
3383166
Revert overwrites 7.
dlpzx Oct 31, 2023
7ed96af
Revert overwrites 8.
dlpzx Oct 31, 2023
c051896
Revert overwrites 9.
dlpzx Oct 31, 2023
f5d62d7
Revert overwrites 10.
dlpzx Oct 31, 2023
3783a95
Revert overwrites 11.
dlpzx Oct 31, 2023
dacba14
Revert overwrites 12.
dlpzx Oct 31, 2023
3b404cd
Revert overwrites 13.
dlpzx Oct 31, 2023
5d0fe68
Fix down revision for migration script
dlpzx Oct 31, 2023
158925a
feat: Enabling S3 bucket share
anushka-singh Nov 2, 2023
d112a21
bugfix: Enabling S3 bucket share
anushka-singh Nov 3, 2023
06edb53
feat: Enabling S3 bucket share - Addressing comments on PR
anushka-singh Nov 8, 2023
f43003c
feat: Enabling S3 bucket share
anushka-singh Nov 10, 2023
4516f4d
feat: Enabling S3 bucket share - Addressing comments on PR
anushka-singh Nov 15, 2023
0f2faf7
feat: Enabling S3 bucket share - Addressing comments on PR
anushka-singh Nov 16, 2023
9b0ab34
Merge branch 'main' into bucket_share_anushka
anushka-singh Nov 16, 2023
7ab6427
feat: Enabling S3 bucket share
anushka-singh Nov 10, 2023
e251fcc
feat: Enabling S3 bucket share - Addressing comments on PR
anushka-singh Nov 15, 2023
e8bfb4b
feat: Enabling S3 bucket share - Addressing comments on PR
anushka-singh Nov 15, 2023
2ff67bc
Merge branch 'main' of https://github.com/anushka-singh/aws-dataall i…
anushka-singh Nov 16, 2023
3254260
Update share.js
anushka-singh Nov 16, 2023
eb8bf3d
Update index.js
anushka-singh Nov 16, 2023
deb2dd4
bugfix: Fixing KMS policy explosion
anushka-singh Nov 20, 2023
7665a5e
Merge branch 'main' into kms_explosion_fix
anushka-singh Nov 21, 2023
bb34b24
bugfix: Fixing KMS policy explosion: Addressing PR comments
anushka-singh Nov 21, 2023
d4486ff
bugfix: Fixing KMS policy explosion: Addressing PR comments
anushka-singh Nov 27, 2023
6be9b02
bugfix: Fixing KMS policy explosion: Addressing PR comments
anushka-singh Nov 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,7 @@ def revoke_share(cls, engine: Engine, share_uri: str):
source_environment=source_environment,
target_environment=target_environment,
source_env_group=source_env_group,
env_group=env_group,
existing_shared_buckets=existing_shared_buckets
env_group=env_group
)
log.info(f"Clean up S3 successful = {clean_up_folders}")

Expand All @@ -219,8 +218,7 @@ def revoke_share(cls, engine: Engine, share_uri: str):
source_environment,
target_environment,
source_env_group,
env_group,
existing_shared_folders
env_group
)
log.info(f'revoking s3 buckets succeeded = {revoked_s3_buckets_succeed}')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import json
import time
from itertools import count

from dataall.core.environment.db.environment_models import Environment, EnvironmentGroup
from dataall.base.db import utils
Expand All @@ -21,6 +22,7 @@
ACCESS_POINT_CREATION_RETRIES = 5
IAM_ACCESS_POINT_ROLE_POLICY = "targetDatasetAccessControlPolicy"
DATAALL_ALLOW_OWNER_SID = "AllowAllToAdmin"
DATAALL_ACCESS_POINT_KMS_DECRYPT_SID = "DataAll-Access-Point-KMS-Decrypt"


class S3AccessPointShareManager:
Expand Down Expand Up @@ -250,7 +252,8 @@ def manage_access_point_and_policy(self):
access_point_arn = s3_client.create_bucket_access_point(self.bucket_name, self.access_point_name)
# Access point creation is slow
retries = 1
while not s3_client.get_bucket_access_point_arn(self.access_point_name) and retries < ACCESS_POINT_CREATION_RETRIES:
while (not s3_client.get_bucket_access_point_arn(self.access_point_name)
and retries < ACCESS_POINT_CREATION_RETRIES):
logger.info(
'Waiting 30s for access point creation to complete..'
)
Expand Down Expand Up @@ -300,7 +303,10 @@ def manage_access_point_and_policy(self):
)
exceptions_roleId = [f'{item}:*' for item in SessionHelper.get_role_ids(
self.source_account_id,
[self.dataset_admin, self.source_env_admin, SessionHelper.get_delegation_role_arn(self.source_account_id)]
[
self.dataset_admin, self.source_env_admin,
SessionHelper.get_delegation_role_arn(self.source_account_id)
]
)]
admin_statement = {
"Sid": DATAALL_ALLOW_OWNER_SID,
Expand All @@ -324,29 +330,41 @@ def update_dataset_bucket_key_policy(self):
'Updating dataset Bucket KMS key policy...'
)
key_alias = f"alias/{self.dataset.KmsAlias}"
kms_client = KmsClient(account_id=self.source_account_id, region=self.source_environment.region)
kms_client = KmsClient(self.source_account_id, self.source_environment.region)
kms_key_id = kms_client.get_key_id(key_alias)
existing_policy = kms_client.get_key_policy(kms_key_id)
target_requester_id = SessionHelper.get_role_id(self.target_account_id, self.target_requester_IAMRoleName)
if existing_policy and f'{target_requester_id}:*' not in existing_policy:
policy = json.loads(existing_policy)
policy["Statement"].append(
{
"Sid": f"{target_requester_id}",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:userId": f"{target_requester_id}:*"
}
}
}
)
kms_client.put_key_policy(kms_key_id, json.dumps(policy))
target_requester_arn = self.get_role_arn(self.target_account_id, self.target_requester_IAMRoleName)
dlpzx marked this conversation as resolved.
Show resolved Hide resolved

if existing_policy:
existing_policy = json.loads(existing_policy)
counter = count()
statements = {item.get("Sid", next(counter)): item for item in existing_policy.get("Statement", {})}
if DATAALL_ACCESS_POINT_KMS_DECRYPT_SID in statements.keys():
logger.info(
f'KMS key policy contains share statement {DATAALL_ACCESS_POINT_KMS_DECRYPT_SID}, '
f'updating the current one')
statements[DATAALL_ACCESS_POINT_KMS_DECRYPT_SID] = (self.add_target_arn_to_statement_principal
(statements[DATAALL_ACCESS_POINT_KMS_DECRYPT_SID],
target_requester_arn))
else:
logger.info(
f'KMS key does not contain share statement {DATAALL_ACCESS_POINT_KMS_DECRYPT_SID}, '
f'generating a new one')
statements[DATAALL_ACCESS_POINT_KMS_DECRYPT_SID] = (self.generate_default_kms_decrypt_policy_statement
(target_requester_arn))
existing_policy["Statement"] = list(statements.values())
else:
logger.info('KMS key policy does not contain any statements, generating a new one')
existing_policy = {
"Version": "2012-10-17",
"Statement": [
self.generate_default_kms_decrypt_policy_statement(target_requester_arn)
]
}
kms_client.put_key_policy(
kms_key_id,
json.dumps(existing_policy)
)

def delete_access_point_policy(self):
logger.info(
Expand Down Expand Up @@ -447,24 +465,33 @@ def delete_target_role_access_policy(
json.dumps(existing_policy),
)

@staticmethod
def delete_dataset_bucket_key_policy(
share: ShareObject,
self,
dataset: Dataset,
target_environment: Environment,
):
logger.info(
'Deleting dataset bucket KMS key policy...'
)
key_alias = f"alias/{dataset.KmsAlias}"
kms_client = KmsClient(account_id=dataset.AwsAccountId, region=dataset.region)
kms_client = KmsClient(dataset.AwsAccountId, dataset.region)
kms_key_id = kms_client.get_key_id(key_alias)
existing_policy = kms_client.get_key_policy(kms_key_id)
target_requester_id = SessionHelper.get_role_id(target_environment.AwsAccountId, share.principalIAMRoleName)
if existing_policy and f'{target_requester_id}:*' in existing_policy:
policy = json.loads(existing_policy)
policy["Statement"] = [item for item in policy["Statement"] if item.get("Sid", None) != f"{target_requester_id}"]
kms_client.put_key_policy(kms_key_id, json.dumps(policy))
existing_policy = json.loads(kms_client.get_key_policy(kms_key_id))
target_requester_arn = self.get_role_arn(self.target_account_id, self.target_requester_IAMRoleName)
counter = count()
statements = {item.get("Sid", next(counter)): item for item in existing_policy.get("Statement", {})}
if DATAALL_ACCESS_POINT_KMS_DECRYPT_SID in statements.keys():
principal_list = self.get_principal_list(statements[DATAALL_ACCESS_POINT_KMS_DECRYPT_SID])
if f"{target_requester_arn}" in principal_list:
principal_list.remove(f"{target_requester_arn}")
if len(principal_list) == 0:
statements.pop(DATAALL_ACCESS_POINT_KMS_DECRYPT_SID)
else:
statements[DATAALL_ACCESS_POINT_KMS_DECRYPT_SID]["Principal"]["AWS"] = principal_list
existing_policy["Statement"] = list(statements.values())
kms_client.put_key_policy(
kms_key_id,
json.dumps(existing_policy)
)

def handle_share_failure(self, error: Exception) -> None:
"""
Expand Down Expand Up @@ -499,3 +526,35 @@ def handle_revoke_failure(self, error: Exception) -> None:
DatasetAlarmService().trigger_revoke_folder_sharing_failure_alarm(
self.target_folder, self.share, self.target_environment
)

@staticmethod
def get_role_arn(target_account_id, target_requester_IAMRoleName):
return f"arn:aws:iam::{target_account_id}:role/{target_requester_IAMRoleName}"

@staticmethod
def generate_default_kms_decrypt_policy_statement(target_requester_arn):
return {
"Sid": f"{DATAALL_ACCESS_POINT_KMS_DECRYPT_SID}",
"Effect": "Allow",
"Principal": {
"AWS": [
f"{target_requester_arn}"
dlpzx marked this conversation as resolved.
Show resolved Hide resolved
]
},
"Action": "kms:Decrypt",
"Resource": "*"
}

def add_target_arn_to_statement_principal(self, statement, target_requester_arn):
principal_list = self.get_principal_list(statement)
if f"{target_requester_arn}" not in principal_list:
principal_list.append(f"{target_requester_arn}")
statement["Principal"]["AWS"] = principal_list
return statement

@staticmethod
def get_principal_list(statement):
principal_list = statement["Principal"]["AWS"]
if isinstance(principal_list, str):
principal_list = [principal_list]
return principal_list
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
DATAALL_READ_ONLY_SID = "DataAll-Bucket-ReadOnly"
DATAALL_ALLOW_OWNER_SID = "AllowAllToAdmin"
IAM_S3BUCKET_ROLE_POLICY = "dataall-targetDatasetS3Bucket-AccessControlPolicy"
DATAALL_BUCKET_KMS_DECRYPT_SID = "DataAll-Bucket-KMS-Decrypt"


class S3BucketShareManager:
Expand Down Expand Up @@ -268,29 +269,33 @@ def grant_dataset_bucket_key_policy(self):
kms_client = KmsClient(self.source_account_id, self.source_environment.region)
kms_key_id = kms_client.get_key_id(key_alias)
existing_policy = kms_client.get_key_policy(kms_key_id)
target_requester_id = SessionHelper.get_role_id(self.target_account_id, self.target_requester_IAMRoleName)
if existing_policy and f'{target_requester_id}:*' not in existing_policy:
policy = json.loads(existing_policy)
policy["Statement"].append(
{
"Sid": f"{target_requester_id}",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:userId": f"{target_requester_id}:*"
}
}
}
)
kms_client.put_key_policy(
kms_key_id,
json.dumps(policy)
)
target_requester_arn = self.get_role_arn(self.target_account_id, self.target_requester_IAMRoleName)
if existing_policy:
existing_policy = json.loads(existing_policy)
counter = count()
statements = {item.get("Sid", next(counter)): item for item in existing_policy.get("Statement", {})}
if DATAALL_BUCKET_KMS_DECRYPT_SID in statements.keys():
logger.info(f'KMS key policy contains share statement {DATAALL_BUCKET_KMS_DECRYPT_SID}, updating the current one')
statements[DATAALL_BUCKET_KMS_DECRYPT_SID] = self.add_target_arn_to_statement_principal(
statements[DATAALL_BUCKET_KMS_DECRYPT_SID], target_requester_arn)
else:
logger.info(
f'KMS key does not contain share statement {DATAALL_BUCKET_KMS_DECRYPT_SID}, generating a new one')
statements[DATAALL_BUCKET_KMS_DECRYPT_SID] = self.generate_default_kms_decrypt_policy_statement(
target_requester_arn)
existing_policy["Statement"] = list(statements.values())
else:
logger.info('KMS key policy does not contain any statements, generating a new one')
existing_policy = {
"Version": "2012-10-17",
"Statement": [
self.generate_default_kms_decrypt_policy_statement(target_requester_arn)
]
}
kms_client.put_key_policy(
kms_key_id,
json.dumps(existing_policy)
)

def delete_target_role_bucket_policy(self):
logger.info(
Expand Down Expand Up @@ -374,11 +379,9 @@ def delete_target_role_access_policy(
json.dumps(existing_policy),
)

@staticmethod
def delete_target_role_bucket_key_policy(
share: ShareObject,
self,
target_bucket: DatasetBucket,
target_environment: Environment,
):
if (target_bucket.imported and target_bucket.importedKmsKey) or not target_bucket.imported:
logger.info(
Expand All @@ -387,15 +390,23 @@ def delete_target_role_bucket_key_policy(
key_alias = f"alias/{target_bucket.KmsAlias}"
kms_client = KmsClient(target_bucket.AwsAccountId, target_bucket.region)
kms_key_id = kms_client.get_key_id(key_alias)
existing_policy = kms_client.get_key_policy(kms_key_id)
target_requester_id = SessionHelper.get_role_id(target_environment.AwsAccountId, share.principalIAMRoleName)
if existing_policy and f'{target_requester_id}:*' in existing_policy:
policy = json.loads(existing_policy)
policy["Statement"] = [item for item in policy["Statement"] if item.get("Sid", None) != f"{target_requester_id}"]
kms_client.put_key_policy(
kms_key_id,
json.dumps(policy)
)
existing_policy = json.loads(kms_client.get_key_policy(kms_key_id))
target_requester_arn = self.get_role_arn(self.target_account_id, self.target_requester_IAMRoleName)
counter = count()
statements = {item.get("Sid", next(counter)): item for item in existing_policy.get("Statement", {})}
if DATAALL_BUCKET_KMS_DECRYPT_SID in statements.keys():
principal_list = self.get_principal_list(statements[DATAALL_BUCKET_KMS_DECRYPT_SID])
if f"{target_requester_arn}" in principal_list:
principal_list.remove(f"{target_requester_arn}")
if len(principal_list) == 0:
statements.pop(DATAALL_BUCKET_KMS_DECRYPT_SID)
else:
statements[DATAALL_BUCKET_KMS_DECRYPT_SID]["Principal"]["AWS"] = principal_list
existing_policy["Statement"] = list(statements.values())
kms_client.put_key_policy(
kms_key_id,
json.dumps(existing_policy)
)

def handle_share_failure(self, error: Exception) -> bool:
"""
Expand Down Expand Up @@ -456,3 +467,17 @@ def generate_default_bucket_read_policy_statement(s3_bucket_name, target_request
f"arn:aws:s3:::{s3_bucket_name}/*"
]
}

@staticmethod
def generate_default_kms_decrypt_policy_statement(target_requester_arn):
return {
"Sid": f"{DATAALL_BUCKET_KMS_DECRYPT_SID}",
"Effect": "Allow",
"Principal": {
"AWS": [
f"{target_requester_arn}"
]
},
"Action": "kms:Decrypt",
"Resource": "*"
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ def process_approved_shares(
sharing_folder.manage_bucket_policy()
sharing_folder.grant_target_role_access_policy()
sharing_folder.manage_access_point_and_policy()
sharing_folder.update_dataset_bucket_key_policy()
if not dataset.imported or dataset.importedKmsKey:
sharing_folder.update_dataset_bucket_key_policy()

new_state = shared_item_SM.run_transition(ShareItemActions.Success.value)
shared_item_SM.update_state_single_item(session, sharing_item, new_state)
Expand Down Expand Up @@ -177,7 +178,6 @@ def clean_up_share(
target_environment: Environment,
source_env_group: EnvironmentGroup,
env_group: EnvironmentGroup,
existing_shared_buckets: bool = False
):
"""
1) deletes S3 access point for this share in this Dataset S3 Bucket
Expand Down Expand Up @@ -209,11 +209,7 @@ def clean_up_share(
dataset=dataset,
target_environment=target_environment
)
if not existing_shared_buckets:
clean_up_folder.delete_dataset_bucket_key_policy(
share=share,
dataset=dataset,
target_environment=target_environment
)
if not dataset.imported or dataset.importedKmsKey:
clean_up_folder.delete_dataset_bucket_key_policy(dataset=dataset)

return True
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def process_approved_shares(
try:
sharing_bucket.grant_role_bucket_policy()
sharing_bucket.grant_s3_iam_access()
sharing_bucket.grant_dataset_bucket_key_policy()
if not dataset.imported or dataset.importedKmsKey:
sharing_bucket.grant_dataset_bucket_key_policy()
new_state = shared_item_SM.run_transition(ShareItemActions.Success.value)
shared_item_SM.update_state_single_item(session, sharing_item, new_state)

Expand All @@ -107,7 +108,6 @@ def process_revoked_shares(
target_environment: Environment,
source_env_group: EnvironmentGroup,
env_group: EnvironmentGroup,
existing_shared_folders: bool = False
) -> bool:
"""
1) update_share_item_status with Start action
Expand Down Expand Up @@ -153,11 +153,9 @@ def process_revoked_shares(
target_bucket=revoked_bucket,
target_environment=target_environment
)
if not existing_shared_folders:
if not dataset.imported or dataset.importedKmsKey:
removing_bucket.delete_target_role_bucket_key_policy(
share=share,
target_bucket=revoked_bucket,
target_environment=target_environment
)
new_state = revoked_item_SM.run_transition(ShareItemActions.Success.value)
revoked_item_SM.update_state_single_item(session, removing_item, new_state)
Expand Down
Loading
Loading