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

922 - Replace IAM inline policies by configurable Managed Policies for folder and bucket sharing #1068

Merged
merged 61 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
5d876ec
added 'create_managed_policy' method to base IAM class
Jan 30, 2024
43d0557
Merge branch 'mda-main' into 922-consumer-ima-role
Feb 8, 2024
d7145ff
generate empty policies, when the cosumption role is added
Feb 9, 2024
7ecb809
detach|delete|update managed policies for consumption roles
Feb 9, 2024
c33a547
add permissions to pivot role to create and attach managed policies
Feb 12, 2024
08df323
fatal typo. Corrected: uri -> env_uri
Feb 12, 2024
06a87d9
ensure always consumptionRoleName and not IAMRoleName is used
Feb 13, 2024
d44409d
lint
Feb 13, 2024
3db3664
compose managed policy arn by name and account_id
Feb 13, 2024
342fa83
detach pilicies from role by IAM role name, not by consumption_role_name
Feb 13, 2024
71b9445
lint
Feb 13, 2024
7ccd505
adjsut share_managers to work with managed policies instead of inline…
Feb 14, 2024
0c881b9
check if the required policies are attached to consumer role before s…
Feb 15, 2024
18c69c8
Flag dataallManaged for the consumptionRoles + migrations. Front-end …
Feb 15, 2024
49f3e05
frontend and test fix
Feb 15, 2024
d4875e0
Block share create if there are no policies attached to consumption role
Feb 15, 2024
4649b18
raise class Exception
Feb 15, 2024
6b6702f
add permissions to pivot role and more logging
Feb 16, 2024
a9fbc00
add permissions to pivot role for Add and Delete policy versions
Feb 16, 2024
93b6f3a
auto-aplly policies, if user checked this option. Display dataallMana…
Feb 16, 2024
b9f5ef4
field dataallManaged typo fix.
Feb 20, 2024
645ea5e
use just one share policy per consumption role
Feb 21, 2024
3ab0988
For Group-role the share policies are also attached
Feb 21, 2024
6a30a1a
lint fix
Feb 21, 2024
da1a3ee
Merge branch 'mda-main' into 922-consumer-ima-role
Feb 21, 2024
a633747
Revert Makefile as it was
Feb 21, 2024
8fa4cf9
Checkov baseline supression of 'CKV_AWS_110' for PivotRolepolicy3. ia…
Feb 22, 2024
73d44ad
disable 'Send request' button, if the policy is not attached
Feb 22, 2024
f202ddb
Merge branch 'mda-main' into 922-consumer-ima-role
Feb 23, 2024
9491131
Merge of alembic migrations
Feb 23, 2024
72c4e92
dataallManaged for ConsumptionRole is set to True by default
Feb 23, 2024
17f9cb8
Correct typo sharePolicyRolName -> sharePolicyRoleName
Feb 23, 2024
e771dd1
Remove ridiculous fields from ConsumptionRoleSearchResult
Feb 23, 2024
dfc9784
All share policy stuff for environment is now in separate service
Feb 23, 2024
cbe9d6d
Backwords compatibility
Feb 23, 2024
38feb93
If there were no inline share policies, we add no resources
Feb 23, 2024
d2b1657
resolver for ConsumptionRole properties
Feb 23, 2024
a31ec06
small changes
Feb 23, 2024
964b44c
Merge branch 'main' into 922-consumer-ima-role
dlpzx Feb 26, 2024
4bdab88
Merge remote-tracking branch 'sof/922-consumer-ima-role' into 922-con…
dlpzx Feb 26, 2024
73b9caa
ManagedPolicies to decouple SharePolicy service from core, backwards …
dlpzx Feb 27, 2024
a9c8a64
Fix alembic migration script
dlpzx Feb 27, 2024
92b98d3
Implementation with abstract classes instead. Added class to data sha…
dlpzx Feb 27, 2024
e7d5571
Uncoupled sharing policies from frontend
dlpzx Feb 27, 2024
77b9982
Added externally managed policies to environment stack to handle non-…
dlpzx Feb 27, 2024
abdeddd
Fix non-imported IAM roles and clear commented backwards compatibilit…
dlpzx Feb 28, 2024
c53dd9e
Divided policies for access points and buckets. Merge ShareManagerUti…
dlpzx Feb 28, 2024
785d002
Fix tests, added methods in remove group from environment and in crea…
dlpzx Feb 28, 2024
45278d9
Revert changes for environment creation
dlpzx Feb 29, 2024
2484ea2
Replace FAKE_S3_PLACEHOLDER by EMPTY_STATEMENT
dlpzx Feb 29, 2024
8fee408
Scope down permissions for pivot role
dlpzx Feb 29, 2024
a7ae29e
Merge branch 'main' into 922-consumer-ima-role
dlpzx Feb 29, 2024
181af2a
Adapt to merge changes
dlpzx Mar 1, 2024
65cf21a
Added exception handling, added and fixed tests
dlpzx Mar 1, 2024
11ae8e1
Fix test
dlpzx Mar 1, 2024
b896e1f
Fix credentials tests
dlpzx Mar 1, 2024
974949d
Small review enhancements
dlpzx Mar 4, 2024
a767146
Add edge cases support: delete policy versions, check policies before…
dlpzx Mar 4, 2024
3ae6892
Fixes backwards compatibility - remove backfilling from environment s…
dlpzx Mar 4, 2024
b96871c
Flaking
dlpzx Mar 4, 2024
3a10763
update checkov and small typo
noah-paige Mar 4, 2024
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
6 changes: 0 additions & 6 deletions .checkov.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@
"CKV_AWS_109",
"CKV_AWS_111"
]
},
{
"resource": "AWS::IAM::ManagedPolicy.PivotRolepolicy3",
"check_ids": [
"CKV_AWS_109"
]
}
]
},
Expand Down
256 changes: 217 additions & 39 deletions backend/dataall/base/aws/iam.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
from botocore.exceptions import ClientError

from .sts import SessionHelper


log = logging.getLogger(__name__)


Expand All @@ -16,12 +16,14 @@ def client(account_id: str, role=None):
def get_role(account_id: str, role_arn: str, role=None):
log.info(f"Getting IAM role = {role_arn}")
try:
iamcli = IAM.client(account_id=account_id, role=role)
response = iamcli.get_role(
client = IAM.client(account_id=account_id, role=role)
response = client.get_role(
RoleName=role_arn.split("/")[-1]
)
assert response['Role']['Arn'] == role_arn, "Arn doesn't match the role name. Check Arn and try again."
except Exception as e:
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to get role {role_arn}: {e}')
log.error(
f'Failed to get role {role_arn} due to: {e}'
)
Expand All @@ -33,71 +35,247 @@ def get_role(account_id: str, role_arn: str, role=None):
def get_role_arn_by_name(account_id: str, role_name: str, role=None):
log.info(f"Getting IAM role name= {role_name}")
try:
iamcli = IAM.client(account_id=account_id, role=role)
response = iamcli.get_role(
client = IAM.client(account_id=account_id, role=role)
response = client.get_role(
RoleName=role_name
)
except Exception as e:
return response["Role"]["Arn"]
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to get role {role_name}: {e}')
log.error(
f'Failed to get role {role_name} due to: {e}'
)
return None
else:
return response["Role"]["Arn"]

@staticmethod
def update_role_policy(
account_id: str,
role_name: str,
policy_name: str,
policy: str,
def get_role_policy(
account_id: str,
role_name: str,
policy_name: str,
):
try:
iamcli = IAM.client(account_id)
iamcli.put_role_policy(
client = IAM.client(account_id)
response = client.get_role_policy(
RoleName=role_name,
PolicyName=policy_name,
PolicyDocument=policy,
)
except Exception as e:
return response["PolicyDocument"]
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to get policy {policy_name} of role {role_name}: {e}')
log.error(
f'Failed to add S3 bucket access to target role {account_id}/{role_name} : {e}'
f'Failed to get policy {policy_name} of role {role_name} : {e}'
)
raise e
return None

@staticmethod
def get_role_policy(
account_id: str,
role_name: str,
policy_name: str,
def delete_role_policy(
account_id: str,
role_name: str,
policy_name: str,
):
try:
iamcli = IAM.client(account_id)
response = iamcli.get_role_policy(
client = IAM.client(account_id)
client.delete_role_policy(
RoleName=role_name,
PolicyName=policy_name,
)
except Exception as e:
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to delete policy {policy_name} of role {role_name}: {e}')
log.error(
f'Failed to get policy {policy_name} of role {role_name} : {e}'
f'Failed to delete policy {policy_name} of role {role_name} : {e}'
)

@staticmethod
def get_managed_policy_by_name(
account_id: str,
policy_name: str
):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)
response = client.get_policy(PolicyArn=arn)
return response['Policy']
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(
f'Data.all Environment Pivot Role does not have permissions to to get policy {policy_name}: {e}')
log.error(
f'Failed to get policy {policy_name}: {e}'
)
return None
else:
return response["PolicyDocument"]

@staticmethod
def delete_role_policy(
account_id: str,
role_name: str,
policy_name: str,
def create_managed_policy(
account_id: str,
policy_name: str,
policy: str
):
try:
iamcli = IAM.client(account_id)
iamcli.delete_role_policy(
RoleName=role_name,
client = IAM.client(account_id)
response = client.create_policy(
PolicyName=policy_name,
PolicyDocument=policy,
)
arn = response['Policy']['Arn']
log.info(
f'Created managed policy {arn}'
)
return arn
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to create managed policy {policy_name}: {e}')
raise Exception(f'Failed to create managed policy {policy_name} : {e}')

@staticmethod
def delete_managed_policy_by_name(
account_id: str,
policy_name
):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)
client.delete_policy(
PolicyArn=arn
)
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to delete managed policy {policy_name}: {e}')
raise Exception(f'Failed to delete managed policy {policy_name} : {e}')

@staticmethod
def get_managed_policy_default_version(
account_id: str,
policy_name: str
):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)
response = client.get_policy(PolicyArn=arn)
versionId = response['Policy']['DefaultVersionId']
policyVersion = client.get_policy_version(PolicyArn=arn, VersionId=versionId)
policyDocument = policyVersion['PolicyVersion']['Document']
return versionId, policyDocument
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to get policy {policy_name}: {e}')
log.error(f'Failed to get policy {policy_name} : {e}')
return None, None

@staticmethod
def update_managed_policy_default_version(
account_id: str,
policy_name: str,
old_version_id: str,
policy_document: str):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)
client.create_policy_version(
PolicyArn=arn,
PolicyDocument=policy_document,
SetAsDefault=True
)

client.delete_policy_version(PolicyArn=arn, VersionId=old_version_id)
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(
f'Data.all Environment Pivot Role does not have permissions to update policy {policy_name}: {e}')
raise Exception(f'Failed to update policy {policy_name} : {e}')

@staticmethod
def delete_managed_policy_non_default_versions(
account_id: str,
policy_name: str,
):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)

# List all policy versions
paginator = client.get_paginator('list_policy_versions')
pages = paginator.paginate(
PolicyArn=arn
)
versions = []
for page in pages:
versions += page['Versions']
non_default_versions = [version['VersionId'] for version in versions if version['IsDefaultVersion'] is False]
# Delete all non-default versions
for version_id in non_default_versions:
client.delete_policy_version(PolicyArn=arn, VersionId=version_id)

return True
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(f'Data.all Environment Pivot Role does not have permissions to get policy {policy_name}: {e}')
log.error(f'Failed to get policy {policy_name} : {e}')
return None, None

@staticmethod
def is_policy_attached(
account_id: str,
policy_name: str,
role_name: str
):
try:
client = IAM.client(account_id)
paginator = client.get_paginator('list_attached_role_policies')
pages = paginator.paginate(
RoleName=role_name
)
except Exception as e:
policies = []
for page in pages:
policies += page['AttachedPolicies']
return policy_name in [p['PolicyName'] for p in policies]
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(
f'Data.all Environment Pivot Role does not have permissions to to get the list of attached policies to the role {role_name}: {e}')
log.error(
f'Failed to delete policy {policy_name} of role {role_name} : {e}'
f'Failed to get the list of attached policies to the role {role_name}: {e}'
)
return False

@staticmethod
def attach_role_policy(
account_id,
role_name,
policy_arn
):
try:
client = IAM.client(account_id)
response = client.attach_role_policy(
RoleName=role_name,
PolicyArn=policy_arn
)
return True
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(
f'Data.all Environment Pivot Role does not have permissions to to attach policy {policy_arn} to the role {role_name}: {e}')
log.error(
f'Failed to attach policy {policy_arn} to the role {role_name}: {e}'
)
raise e

@staticmethod
def detach_policy_from_role(
account_id: str,
role_name: str,
policy_name: str):
try:
arn = f'arn:aws:iam::{account_id}:policy/{policy_name}'
client = IAM.client(account_id)
client.detach_role_policy(
RoleName=role_name,
PolicyArn=arn
)
except ClientError as e:
if e.response['Error']['Code'] == 'AccessDenied':
raise Exception(
f'Data.all Environment Pivot Role does not have permissions to detach policy {policy_name} from role {role_name}: {e}')
raise Exception(f'Failed to detach policy {policy_name} from role {role_name}: {e}')
3 changes: 2 additions & 1 deletion backend/dataall/base/utils/naming_convention.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
class NamingConventionPattern(Enum):

S3 = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 63}
IAM = {'regex': '[^a-zA-Z0-9-_]', 'separator': '-', 'max_length': 63}
IAM = {'regex': '[^a-zA-Z0-9-_]', 'separator': '-', 'max_length': 63} # Role names up to 64 chars
IAM_POLICY = {'regex': '[^a-zA-Z0-9-_]', 'separator': '-', 'max_length': 128} # Policy names up to 128 chars
GLUE = {'regex': '[^a-zA-Z0-9_]', 'separator': '_', 'max_length': 240} # Limit 255 - 15 extra chars buffer
GLUE_ETL = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 52}
NOTEBOOK = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 63}
Expand Down
2 changes: 2 additions & 0 deletions backend/dataall/core/environment/api/input_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ class EnvironmentSortField(GraphQLEnumMapper):
gql.Argument('groupUri', gql.NonNullableType(gql.String)),
gql.Argument('IAMRoleArn', gql.NonNullableType(gql.String)),
gql.Argument('environmentUri', gql.NonNullableType(gql.String)),
gql.Argument('dataallManaged', gql.NonNullableType(gql.Boolean)),

],
)

Expand Down
Loading
Loading