Sviluppo remoto su AWS: da Cloud9 a VS Code
20 Novembre 2024 - 2 min. read
Alessio Gandini
Cloud-native Development Line Manager
--- AWSTemplateFormatVersion: '2010-09-09' Description: | CloudFormation and IAM Permission Boundaries Demo ################################################################################ # Metadata # ################################################################################ Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: {default: 'Required parameters'} Parameters: - VpcId - AmiId - KeyName - SubnetId - Label: {default: 'Optional parameters'} Parameters: - NameSpace - ProjectName - Environment ################################################################################ # Parameters # ################################################################################ Parameters: NameSpace: Type: String Default: 'besharp' ProjectName: Type: String Default: 'permission-boundaries-demo' Environment: Type: String Default: 'dev' VpcId: Type: AWS::EC2::VPC::Id SubnetId: Type: AWS::EC2::Subnet::Id AmiId: Type: AWS::EC2::Image::Id KeyName: Type: AWS::EC2::KeyPair::KeyName ################################################################################ # Conditions # ################################################################################ Conditions: {} ################################################################################ # Mappings # ################################################################################ Mappings: {} ################################################################################ # Resources # ################################################################################ Resources: #################################### S3 #################################### S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub 'com.${NameSpace}.${ProjectName}' Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}' - Key: Owner Value: 'name.surname@besharp.it' ################################### EC2 #################################### EC2Instance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref IAMInstanceProfile ImageId: !Ref AmiId InstanceType: t3a.micro KeyName: !Ref KeyName NetworkInterfaces: - AssociatePublicIpAddress: 'true' DeviceIndex: '0' GroupSet: - !Ref EC2SecurityGroup SubnetId: !Ref SubnetId Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}' - Key: Owner Value: 'name.surname@besharp.it' EC2SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub '${NameSpace}-${ProjectName}-ec2' GroupDescription: !Sub 'Security Group for ${NameSpace}-${ProjectName}-ec2' VpcId: !Ref VpcId Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}-ec2' - Key: Owner Value: 'name.surname@besharp.it' ################################### IAM #################################### IAMInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Roles: - !Ref IAMRole InstanceProfileName: !Sub '${NameSpace}-${ProjectName}' IAMRole: Type: AWS::IAM::Role Properties: RoleName: !Sub '${NameSpace}-${ProjectName}' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole Path: '/' Policies: - PolicyName: 'EC2Access' PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - 's3:GetObject' Resource: !Sub '${S3Bucket.Arn}/*' Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}' - Key: Owner Value: 'name.surname@besharp.it' ################################################################################ # Outputs # ################################################################################ Outputs: StackName: Description: 'Stack name.' Value: !Sub '${AWS::StackName}'Al suo interno sono presenti le informazioni per creare:Bucket S3Istanza EC2 + Security GroupRuolo IAM + Instance ProfileAllo sviluppatore è stata attaccata la seguente policy per poter deployare questo template:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "CloudFormationReadAccess", "Effect": "Allow", "Action": [ "cloudformation:DescribeStacks", "cloudformation:ListChangeSets", "cloudformation:ListExports", "cloudformation:ListImports", "cloudformation:ListStacks" ], "Resource": "*" }, { "Sid": "CloudFormationWriteAccess", "Effect": "Allow", "Action": [ "cloudformation:CreateStack", "cloudformation:DeleteStack", "cloudformation:TagResource", "cloudformation:UpdateStack", "cloudformation:ValidateTemplate" ], "Resource": "*" }, { "Sid": "EC2ReadAccess", "Effect": "Allow", "Action": [ "ec2:DescribeImages", "ec2:DescribeInstances", "ec2:DescribeKeyPairs", "ec2:DescribeSecurityGroupReferences", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeVpcs" ], "Resource": "*" }, { "Sid": "EC2WriteAccess", "Effect": "Allow", "Action": [ "ec2:AssociateIamInstanceProfile", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:CreateSecurityGroup", "ec2:CreateTags", "ec2:DeleteSecurityGroup", "ec2:DeleteTags", "ec2:RevokeSecurityGroupEgress", "ec2:RevokeSecurityGroupIngress", "ec2:RunInstances", "ec2:StartInstances", "ec2:StopInstances", "ec2:TerminateInstances" ], "Resource": "*" }, { "Sid": "IAMReadAccess", "Effect": "Allow", "Action": [ "iam:GetInstanceProfile", "iam:GetRole", "iam:GetRolePolicy" ], "Resource": "*" }, { "Sid": "IAMWriteAccess", "Effect": "Allow", "Action": [ "iam:AddRoleToInstanceProfile", "iam:CreateInstanceProfile", "iam:CreateRole", "iam:DeleteInstanceProfile", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:PassRole", "iam:PutRolePolicy", "iam:RemoveRoleFromInstanceProfile", "iam:TagRole", "iam:UntagRole" ], "Resource": "*" }, { "Sid": "S3WriteAccess", "Effect": "Allow", "Action": [ "s3:CreateBucket", "s3:DeleteBucket", "s3:PutBucketTagging" ], "Resource": "*" } ] }Connettendoci all’istanza EC2 creata, possiamo verificare che effettivamente possa scaricare oggetti dal bucket S3 appena creato:
ubuntu@ec2-demo:~$ aws s3 cp s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine . download: s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine to ./if-you-download-me-you-are-fine ubuntu@ec2-demo:~$ cat if-you-download-me-you-are-fine test-okE che non possa, ad esempio, creare altri bucket:
ubuntu@ec2-demo:~$ aws s3api create-bucket --bucket can-i-create-it An error occurred (AccessDenied) when calling the CreateBucket operation: Access DeniedA questo punto, lo sviluppatore potrebbe (a prescindere dalla buona o cattiva fede) voler modificare i permessi del ruolo associato alla macchina per poter effettuare operazioni che di norma non è autorizzato ad eseguire (o addirittura crearsi un ruolo di amministrazione da assumere). La risorsa relativa allo IAM Role può essere modificata così:
IAMRole: Type: AWS::IAM::Role Properties: RoleName: !Sub '${NameSpace}-${ProjectName}' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole Path: '/' Policies: - PolicyName: 'AdministratorAccess' PolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Action: - '*' Resource: '*' Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}'In tal modo, ad esempio, l’istanza può scaricare oggetti dal bucket, ma anche crearne di altri (senza contare che, con la policy modificata rozzamente come da esempio, di fatto, ha permessi di amministratore all’interno dell’account):
ubuntu@ec2-demo:~$ aws s3 cp s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine . download: s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine to ./if-you-download-me-you-are-fine ubuntu@ec2-demo:~$ cat if-you-download-me-you-are-fine test-ok ubuntu@ec2-demo:~$ aws s3api create-bucket --bucket can-i-create-it { "Location": "/can-i-create-it" }Chiaramente la preoccupazione principale non è data solamente dal fatto che lo sviluppatore possa acquisire permessi non previsti, ma anche dall’effettivo aumento della superficie attaccabile da malintenzionati. Non è nuovo infatti il concetto di privilege escalation, nel quale una persona anche esterna all’azienda va a sfruttare dei permessi inutilmente ampi per predisporre il terreno fertile ad un attacco organizzato.
{ "Sid": "IAMPermissionBoundaryWriteAccess", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:PutRolePolicy", "iam:UpdateRole", "iam:UpdateRoleDescription" ], "Resource": "arn:aws:iam::111122223333:role/dev-namespace/*", "Condition": { "StringEquals": { "iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/besharp-permission-boundary-demo" } } }, { "Sid": "IAMPassRoleAccess", "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": "arn:aws:iam::111122223333:role/dev-namespace/*" }, { "Sid": "IAMWriteAccess", "Effect": "Allow", "Action": [ "iam:AddRoleToInstanceProfile", "iam:CreateInstanceProfile", "iam:DeleteInstanceProfile", "iam:DeleteRole", "iam:DeleteRolePolicy", "iam:RemoveRoleFromInstanceProfile", "iam:TagRole", "iam:UntagRole" ], "Resource": "*" },Se al momento della creazione del ruolo da parte di CloudFormation non è presente il permission boundary, la creazione fallirà.Per agganciare il permission boundary allo IAM role dell’istanza, la risorsa va modificata come segue:
IAMRole: Type: AWS::IAM::Role Properties: RoleName: !Sub '${NameSpace}-${ProjectName}' PermissionsBoundary: !Ref PermissionBoundaryArn AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole Path: '/dev-namespace/' Policies: - PolicyName: 'AdministratorAccess' PolicyDocument: Version: '2012-10-17' Statement: Effect: 'Allow' Action: - '*' Resource: '*' Tags: - Key: Name Value: !Sub '${NameSpace}-${ProjectName}' - Key: Owner Value: 'name.surname@besharp.it'Nota: per rendere il tutto più sicuro e limitare l’azione particolarmente sensibile di iam:PassRole è stato aggiunto anche il concetto di Path, che essenzialmente va ad identificare un namespace per gli sviluppatori all’interno di IAMAgganciando il permission boundary, la creazione avrà successo e i permessi dell’istanza (e di qualunque altro ruolo lo sviluppatore voglia creare) saranno limitati.
ubuntu@ec2-demo:~$ aws s3 cp s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine . download: s3://com.besharp.permission-boundaries-demo/if-you-download-me-you-are-fine to ./if-you-download-me-you-are-fine ubuntu@ec2-demo:~$ cat if-you-download-me-you-are-fine test-ok ubuntu@ec2-demo:~$ aws s3api create-bucket --bucket can-i-create-it An error occurred (AccessDenied) when calling the CreateBucket operation: Access DeniedNella configurazione presentata è chiaro come il permission boundary diventi la risorsa sotto controllo del team di sicurezza, il quale può concordare l’insieme massimo di permessi attribuibili ad una determinata risorsa. Una volta individuati tali permessi, AWS IAM garantirà che l’entità IAM associata ad una risorsa non possa effettuare azioni non previste.