Towards a more Restricted Set of AWS IAM Permissions for Packer
Packer allows us to build images for different platforms, one of them being
AWS. AWS images are built within the AWS environment, which means that Packer
requires permissions to AWS in order to build an image. If we look at the
official documentation it gives us the set of required permissions with
"Resource": "*"
. This applies to all API endpoints they use, including for
example DeleteKeyPair
, DeregisterImage
and TerminateInstances
.
I do not really feel comfortable if an external program has permissions to delete any of my production resources. Thus, in this blog post I want to see what I can achieve to further restrict these permissions.
Let’s go through the permissions step by step. The official policy document in the Packer docs defines these required permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CopyImage",
"ec2:CreateImage",
"ec2:CreateKeypair",
"ec2:CreateSecurityGroup",
"ec2:CreateSnapshot",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:DeleteKeyPair",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSnapshot",
"ec2:DeleteVolume",
"ec2:DeregisterImage",
"ec2:DescribeImageAttribute",
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeRegions",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DetachVolume",
"ec2:GetPasswordData",
"ec2:ModifyImageAttribute",
"ec2:ModifyInstanceAttribute",
"ec2:ModifySnapshotAttribute",
"ec2:RegisterImage",
"ec2:RunInstances",
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Resource": "*"
}
]
}
The least dangerous ones depending on your threat models probably are
Describe
and Create
actions. With Describe
actions it might be possible
that Packer reads the layout of your infrastructure and accidentally publishes
it. With Create
actions on the other hand it could happen that Packer creates
some resources, does not delete them properly and thus causes additional
cost.
The more dangerous ones for production environments are the
Modify
, Delete
, Detach
, Deregister
,
Stop
and Terminate
ones. These allow Packer to change currently running
production infrastructure, or in the worst case even take it down. In my
opinion this is something we should not allow and thus we need to modify
the set of IAM permissions.
In addition we have a few more actions with other prefixes: AttachVolume
,
AuthorizeSecurityGroupIngress
, CopyImage
, GetPasswordData
,
RegisterImage
, and RunInstances
.
SSH Key Pair
First of all we can entirely get rid of the CreateKeyPair
and DeleteKeyPair
permissions by using ssh_keypair_name
and ssh_private_key_file
in Packer.
ssh_keypair_name
is the name of a key in your AWS account while
ssh_private_key_file
must point to the corresponding private key on your local
computer.
So, let’s create a new private key pair for packer:
ssh-keygen -f /home/brati/.ssh/id_rsa_packer -N "" -C packer
cat /home/brati/.ssh/id_rsa_packer.pub
Next, import the public key into your EC2 key pair list (do not create a new key pair, as this would generate a different private key).
Add the following two lines to your Packer source
block and delete the
CreateKeyPair
and DeleteKeyPair
permissions from your Packer IAM policy.
ssh_keypair_name = "packer"
ssh_private_key_file = "~/.ssh/id_rsa_packer"
Security Group
Similar to the SSH Key Pair we can also use a pre-created security group for
Packer. This allows us to get rid of the security group related permissions,
namely CreateSecurityGroup
, DeleteSecurityGroup
, and AuthorizeSecurityGroupIngress
.
The permission DescribeSecurityGroups
is still required, because
Packer uses it to verify whether the specified security group actually exists.
So, create a security group named packer
with SSH ingress permissions
(most probably from anywhere, but can also be a static IP if Packer is running
on one of your servers). Take a note of the new security group ID and
add a line with the security group ID to your packer config:
security_group_id = "sg-0123456789abcdef0"
Delete Permissions
Next, I want to tackle the delete permissions. I understand that Packer needs
to be able to cleanup resources that it creates during creation of the image.
I also understand that of course it’s not possible to get rid of the creation
of a new instance to create the image, i.e. at least the TerminateInstances
call is necessary. However, I feel uneasy when Packer is allowed to delete
production resources.
Thus, my idea is to restrict Packer’s delete permissions to resources with a specific tag that must be created by Packer when allocating the resources. That way, Packer will never be able to delete resources which it did not create (unless someone else uses the same tag during creation of resources).
For this we can define the settings run_tags
and run_volume_tags
in Packer.
These options will add tags to the volumes and instances that get created
by Packer for the AMI creation process. snapshot_tags
and tags
will apply
tags to the snapshots and image of the created AMI.
This allows us to make the deletion actions DeleteSnapshot
, DeleteVolume
,
DeregisterImage
, StopInstances
and TerminateInstances
a bit safer.
Also the modification actions ModifyImageAttribute
, ModifyInstanceAttribute
,
and ModifySnapshotAttribute
should profit from this change,
but during my tests it seems these were not used by Packer at all.
I would
also define AttachVolume
and DetachVolume
to be a bit unsafe, because
Packer could mount and unmount production volumes. Ideally, I would like to
restrict Packer to mount volumes with the tag Creator=Packer
on instances
with the tag Creator=Packer
, but I am not sure whether I can define such
a two-level constraint with IAM request conditions.
Add the following lines to your Packer source
block:
run_tags = {
Creator = "Packer"
}
run_volume_tags = {
Creator = "Packer"
}
snapshot_tags = {
Creator = "Packer"
}
tags = {
Creator = "Packer"
}
Adjust your IAM permissions and add a new section with more restricted permissions (this already includes the changes we performed in the previous sections):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RatherSafeActions",
"Effect": "Allow",
"Action": [
"ec2:CopyImage",
"ec2:CreateImage",
"ec2:CreateSnapshot",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:DescribeImages",
"ec2:DescribeImageAttribute",
"ec2:DescribeInstanceStatus",
"ec2:DescribeInstances",
"ec2:DescribeRegions",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeSubnets",
"ec2:DescribeVolumes",
"ec2:DescribeTags",
"ec2:RegisterImage",
"ec2:RunInstances",
"ec2:GetPasswordData"
],
"Resource": "*"
},
{
"Sid": "DangerousActions",
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:DeleteSnapshot",
"ec2:DeleteVolume",
"ec2:DeregisterImage",
"ec2:DetachVolume",
"ec2:ModifyImageAttribute",
"ec2:ModifyInstanceAttribute",
"ec2:ModifySnapshotAttribute",
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/Creator": "Packer"
}
}
}
]
}
Further Improvements
Depending on your Packer definitions you might be able to further reduce
these permissions. As mentioned I think that the Modify
actions were not
used in my case, so I might be able to delete them and still get AMIs.
Moreover, the permission GetPasswordData
seems to be
only required on Windows VMs.
Thus, if you only want to build Linux images you can delete this permission.
If you want to keep it, restricting to instances created by Packer does not seem
to be required as to my understanding
retrieving the password from a Windows host works by decrypting the password
with the SSH key pair.
I.e. the Packer SSH key can only decrypt passwords from Windows hosts that were
created with the Packer SSH key pair.
I recently heard the idea of tagging resources with a autodelete
marker and
then automatically deleting them. During research for this article I also
saw an AWS blog article mentioning IAM policies that enforce definition of
tags. Combining these two we could extend the IAM policy to only allow
Packer to create resources with the tag Creator: Packer
or maybe even a tag
Autodelete: [some date]
.
Depending on the regions you are using it can also make sense to use resource-based restrictions to restrict all actions to one or more AWS regions.
Packer Definition
I based my tests on the Packer tutorial. The final HCL definition looks like this:
packer {
required_plugins {
amazon = {
version = ">= 0.0.1"
source = "github.com/hashicorp/amazon"
}
}
}
source "amazon-ebs" "ubuntu" {
ami_name = "learn-packer-linux-aws"
instance_type = "t2.micro"
region = "eu-central-1"
source_ami_filter {
filters = {
name = "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
}
ssh_username = "ubuntu"
ssh_keypair_name = "packer"
ssh_private_key_file = "~/.ssh/id_rsa_packer"
security_group_id = "sg-0fe7ca268edf55a34"
run_tags = {
Creator = "Packer"
}
run_volume_tags = {
Creator = "Packer"
}
snapshot_tags = {
Creator = "Packer"
}
tags = {
Creator = "Packer"
}
}
build {
sources = [
"source.amazon-ebs.ubuntu"
]
provisioner "shell" {
environment_vars = [
"FOO=hello world",
]
inline = [
"echo Installing Redis",
"sleep 30",
"sudo apt-get update",
"sudo apt-get install -y redis-server",
"echo \"FOO is $FOO\" > example.txt",
]
}
}