Anti-pattern FinOps: cosa (non) fare per avere più budget per innovare
22 Ottobre 2025 - 9 min. read
Andrea Pusineri
& Nicola Ferrari
& Nicola Ferrari
Giorno dopo giorno sempre più aziende si affacciano con interesse al mondo del cloud computing, molte delle quali si interrogano su come effettivamente possa aiutare il proprio business e su quali problemi di sicurezza si possono introdurre. Una delle maggiori preoccupazioni dei più tecnici è come garantire il maggior grado di libertà possibile offerto dall’agilità che tali tecnologie mettono a disposizione senza introdurre però falle nella sicurezza.Tra gli argomenti più caldi troviamo sicuramente il concetto di Infrastructure as Code (IaC) che di fatto permette di gestire le proprie risorse in cloud tramite sistemi di templating, aprendo quindi il vaso di Pandora di astrazione, ripetibilità e riproducibilità dell’infrastruttura stessa ma fornendo anche strumenti a supporto di attività di Disaster Recovery.
I più lungimiranti sapranno certamente che per deployare tale infrastruttura è necessaria la creazione di uno IAM Role, ma dobbiamo escludere la possibilità che lo sviluppatore possa crearsi un ruolo con permessi da amministratore da assumere per evitare i soliti “noiosi” messaggi di “Access Denied” e “Forbidden”.---
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.