AWS permissions
Give CloudDesk only what it uses.
It needs far less than an administrator’s key. These two policies name every AWS action the app calls, and nothing else: no wildcards, so each line says what it’s for.
The read policy
Give an account this alone to watch it: the fleet, its costs and its alerts, with nothing CloudDesk can do to it.
- Servers
Lists instances in every enabled region, with their sizes and attached volumes.
- ec2:DescribeInstanceTypes
- ec2:DescribeInstances
- ec2:DescribeRegions
- ec2:DescribeVolumes
- Databases
Lists RDS databases, with their engine, replicas and pending changes.
- rds:DescribeDBInstances
- LoadBalancers
Lists load balancers and their target groups, and reads each target’s health.
- elasticloadbalancing:DescribeLoadBalancers
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeTargetHealth
- Buckets
Lists buckets, and reads each one’s region, versioning, policy and public access, and the account’s Block Public Access.
- s3:GetAccountPublicAccessBlock
- s3:GetBucketLocation
- s3:GetBucketPolicy
- s3:GetBucketPolicyStatus
- s3:GetBucketPublicAccessBlock
- s3:GetBucketVersioning
- s3:ListAllMyBuckets
- BucketContents
Lists and reads objects, to browse, preview and download them. Listing an upload’s parts is the one read here that serves a write: a cancelled upload is checked until S3 says it’s gone.
- s3:GetObject
- s3:ListBucket
- s3:ListMultipartUploadParts
- Metrics
Reads CloudWatch metrics for servers and databases, and each bucket’s daily size.
- cloudwatch:GetMetricData
- cloudwatch:GetMetricStatistics
- Billing
Reads Cost Explorer: the month so far, and costs by resource where that’s been switched on. AWS charges a cent a request, so CloudDesk keeps each answer six hours.
- ce:GetCostAndUsage
- ce:GetCostAndUsageWithResources
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Servers",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstanceTypes",
"ec2:DescribeInstances",
"ec2:DescribeRegions",
"ec2:DescribeVolumes"
],
"Resource": "*"
},
{
"Sid": "Databases",
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances"
],
"Resource": "*"
},
{
"Sid": "LoadBalancers",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth"
],
"Resource": "*"
},
{
"Sid": "Buckets",
"Effect": "Allow",
"Action": [
"s3:GetAccountPublicAccessBlock",
"s3:GetBucketLocation",
"s3:GetBucketPolicy",
"s3:GetBucketPolicyStatus",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketVersioning",
"s3:ListAllMyBuckets"
],
"Resource": "*"
},
{
"Sid": "BucketContents",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:ListMultipartUploadParts"
],
"Resource": "*"
},
{
"Sid": "Metrics",
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricData",
"cloudwatch:GetMetricStatistics"
],
"Resource": "*"
},
{
"Sid": "Billing",
"Effect": "Allow",
"Action": [
"ce:GetCostAndUsage",
"ce:GetCostAndUsageWithResources"
],
"Resource": "*"
}
]
}
The operate policy
Add this to the read policy to act from CloudDesk. Each statement is one kind of action, so a copy can keep only the ones you want.
- PowerServers
Starts, stops and restarts instances.
- ec2:RebootInstances
- ec2:StartInstances
- ec2:StopInstances
- ImageServers
Images an instance as an AMI, without rebooting it.
- ec2:CreateImage
- DestroyServers
Terminates an instance, once you’ve typed its name. Delete this statement if CloudDesk should never be able to.
- ec2:TerminateInstances
- PowerDatabases
Starts, stops and reboots RDS databases.
- rds:RebootDBInstance
- rds:StartDBInstance
- rds:StopDBInstance
- WriteBuckets
Uploads objects, in parts when they’re large, cancels an upload, and deletes objects by key.
- s3:AbortMultipartUpload
- s3:DeleteObject
- s3:PutObject
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PowerServers",
"Effect": "Allow",
"Action": [
"ec2:RebootInstances",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*"
},
{
"Sid": "ImageServers",
"Effect": "Allow",
"Action": [
"ec2:CreateImage"
],
"Resource": "*"
},
{
"Sid": "DestroyServers",
"Effect": "Allow",
"Action": [
"ec2:TerminateInstances"
],
"Resource": "*"
},
{
"Sid": "PowerDatabases",
"Effect": "Allow",
"Action": [
"rds:RebootDBInstance",
"rds:StartDBInstance",
"rds:StopDBInstance"
],
"Resource": "*"
},
{
"Sid": "WriteBuckets",
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:DeleteObject",
"s3:PutObject"
],
"Resource": "*"
}
]
}
Attaching them
Make them policies in the account, attach them to the user or role CloudDesk will use, and connect its profile in the app. With IAM Identity Center, add them to a permission set as inline policies instead.
Together they come to under 2,048 characters without whitespace, so they also fit as inline policies on a single user.
To trim them, delete statements from your copy. DestroyServers is the one to delete if CloudDesk should never be able to terminate an instance, and BucketContents if it should see buckets but not read what’s in them. What a policy doesn’t allow, the app says it isn’t allowed to do, rather than failing obscurely.
curl -O https://clouddesk.thaicuong.me/aws-permissions/clouddesk-read.json
curl -O https://clouddesk.thaicuong.me/aws-permissions/clouddesk-operate.json
aws iam create-policy --policy-name CloudDeskRead \
--policy-document file://clouddesk-read.json
aws iam create-policy --policy-name CloudDeskOperate \
--policy-document file://clouddesk-operate.jsonHow they stay true
A policy kept by hand drifts both ways: a new feature calls an action nobody granted, or an old one leaves a grant behind. So a test in CloudDesk finds every call its code makes into an AWS SDK, and fails if a policy lacks a permission one needs, or grants one nothing calls.
They’ve also been put to AWS itself, each as the only permissions of a borrowed credential. Under the read policy alone, every read the app makes was let through, and every server and database write was refused. Under the operate policy alone, the database writes were let through. Its server and bucket writes haven’t been asked that way yet: EC2 needs a real instance to ask about, and S3 can’t be asked without writing.
Encrypted disks and files
Two things depend on your account rather than on CloudDesk. Starting a server whose disks are encrypted with a customer-managed KMS key also needs KMS permissions on that key, which AWS lists under EBS encryption; the default aws/ebs key needs nothing extra.
Files encrypted with a customer-managed key likewise need kms:Decrypt to read them and kms:GenerateDataKey to write them.