Managing policies
Now that we are able to create roles, we would like to manage the permissions of these roles. That's what policies are for.
Creating a new policy
To create a new policy, first we need to write one.
For the sake of this tutorial, let's say that we would like to have the marketing role being able to upload, download and list all the objects in the marketing-docs
and have only the right to download and list objects in the sales-docs
.
To do so we would write the following policy. Open a text editor and in a file named marketing-policy
paste the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": [
"arn:demo:s3:::marketing-docs",
"arn:demo:s3:::marketing-docs/*"
]
},
{
"Effect": "Allow",
"Action": ["s3:Get*", "s3:List*"],
"Resource": ["arn:demo:s3:::sales-docs", "arn:demo:s3:::sales-docs/*"]
}
]
}
As we can see we have two different Statement
in this policy, let's have a look at the first one:
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:demo:s3:::marketing-docs",
"arn:demo:s3:::marketing-docs/*"
]
},
The Effect
element is Allow
, meaning that this Statement
gives the permission to do something.
The Action
element is s3:*
, the *
is a wildcard that can match to anything. So this action means "any action on s3".
The Resource
element is a new one, it basically target on which resource the Action
can (or can't depending on the Effect
) be performed. In this case it says we can perform all actions on the bucket "arn:demo:s3:::marketing-docs"
and on all the objects in the marketing-docs bucket "arn:demo:s3:::marketing-docs/*"
.
Note that this ARN does not have the account Id in it, unlike ARNs that are refering to IAM resources
.
Now let's read the second statement:
{
"Effect": "Allow",
"Action": ["s3:Get*", "s3:List*"],
"Resource": ["arn:demo:s3:::sales-docs", "arn:demo:s3:::sales-docs/*"]
}
The Effect
element is Allow
, meaning that this Statement
gives the permission to do something.
The Action
element has two actions s3:Get*
and s3:List
. This means that the caller can perform any s3 call action that starts with a Get
or a List
.
The Resource
element says we can perform any actions that starts with Get or List on the bucket "arn:demo:s3:::sales-docs"
and on all the objects in the sales-docs bucket "arn:demo:s3:::sales-docs/*"
.
Let's create that policy by running the following command (as root):
aws --profile astran iam create-policy --policy-name marketingPolicy --policy-document file://marketing-policy
This should give you the following output:
{
"Policy": {
"PolicyName": "marketingPolicy",
"PolicyId": "ANPA0D9040E6158F48FCA4389896C53EA2CF",
"Arn": "arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"CreateDate": "2024-08-22T15:14:22.426880+00:00",
"UpdateDate": "2024-08-22T15:14:22.426880+00:00"
}
}
Attach a policy to a role
Now that we have created our policy, let's attach it to our marketing
role by running the following command (as root):
# Replace the policy arn with your policy arn created in the previous step
aws --profile astran iam attach-role-policy --role-name marketing --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy
If everything went well, you should not get any output.
Now let's try to see if our policy really works !
First as root let's create the buckets marketing-docs
and sales-docs
and upload an object in each of them.
aws --profile astran s3 mb s3://marketing-docs
aws --profile astran s3 mb s3://sales-docs
echo "I'm a file in marketing" > file
aws --profile astran s3 cp file s3://marketing-docs/file
echo "I'm a file in sales" > file
aws --profile astran s3 cp file s3://sales-docs/file
Let's try to download the file from each bucket with our marketing
role. If you don't have the credentials created in the previous part of the tutorial (or if they have expired), redo the authentication with the marketing role and then run the following commands:
# Use the URL for your partition
aws --endpoint-url https://demo.s3.astran.io s3 cp s3://marketing-docs/file marketing-file
aws --endpoint-url https://demo.s3.astran.io s3 cp s3://sales-docs/file sales-file
cat marketing-file
# Should print I'm a file in marketing
cat sales-file
# Should print I'm a file in sales
Great we can download files, let's now try to upload a file into the marketing-docs
bucket:
# Use the URL for your partition
echo "A whole new file" > newfile
aws --endpoint-url https://demo.s3.astran.io s3 cp newfile s3://marketing-docs/newfile
You should get the following output:
upload: ./newfile to s3://marketing-docs/newfile
Now try to upload a file in the sales-docs
bucket:
# Use the URL for your partition
aws --endpoint-url https://demo.s3.astran.io s3 cp newfile s3://sales-docs/newfile
You should get the following error:
upload failed: ./newfile to s3://sales-docs/newfile An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
Great it means that our policy works as intended!
Updating a policy
Let's say we have changed our mind, and we have decided that the marketing
role should no longer have access to the sales-docs
. In fact we want to make sure that even if someone were to create a policy that should allow the marketing role to access the sales-docs
bucket, it would not work.
Let's open a text editor and update our marketing-policy
file with the following content:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": [
"arn:demo:s3:::marketing-docs",
"arn:demo:s3:::marketing-docs/*"
]
},
{
"Effect": "Deny",
"Action": ["s3:*"],
"Resource": ["arn:demo:s3:::sales-docs", "arn:demo:s3:::sales-docs/*"]
}
]
}
A couple of things have changed in this new policy:
- The second statement has now a
Effect: "Deny"
, which means it's an explicit Deny. - The action of the second statement has been changed to
s3:*
.
This means that all s3 calls are not denied on the arn:demo:s3:::sales-docs
bucket and the objects in it arn:demo:s3:::sales-docs/*
.
Now you might ask, why didn't we just remove the second statement since by default if there's no policy that applies to the specific resource, we have a Deny
?
The reason is that we want to make sure that even if someone creates a policy that should give you permission to access the sales-docs
bucket, it would get denied. Why ? That's because of how policies are evaluated. An explicit deny always takes priority.
To update the policy, we need to create a new version of the existing policy (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam create-policy-version --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy --policy-document file://marketing-policy --set-as-default
Which will give you the following output:
{
"PolicyVersion": {
"VersionId": "v2",
"IsDefaultVersion": true,
"CreateDate": "2024-08-22T16:02:23.460863+00:00"
}
}
If you don't specify the --set-as-default
parameter, a new version of the policy will be created, but the old policy is the one that will be applied. In this case the IsDefaultVersion
in the previous output would be set to false
If we try to download a file from the sales-docs
bucket it will now fail:
# Replace the url with the url used on your partition
aws --endpoint-url https://demo.s3.astran.io s3 cp s3://sales-docs/file sales-file
Output:
download failed: s3://sales-docs/file to ./sales-file An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
Listing all policies
To list all the policies run the following command (as root):
aws --profile astran iam list-policies
Which should give you the following output:
{
"Policies": [
{
"PolicyName": "AstranS3ReadOnlyAccess",
"PolicyId": "ANPAIZTJ4DXE7G6AGAE6M",
"Arn": "arn:demo:iam::astran:policy/AstranS3ReadOnlyAccess",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"Description": "Provides read only access to all buckets via the Astran explorer",
"CreateDate": "2024-07-01T07:34:07.832321+00:00",
"UpdateDate": "2024-07-01T07:34:07.832321+00:00"
},
{
"PolicyName": "marketingPolicy",
"PolicyId": "ANPA0D9040E6158F48FCA4389896C53EA2CF",
"Arn": "arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy",
"Path": "/",
"DefaultVersionId": "v2",
"AttachmentCount": 1,
"CreateDate": "2024-08-22T15:14:22.426880+00:00",
"UpdateDate": "2024-08-22T16:08:41.796563+00:00"
}
]
}
You should notice that we have a policy that we haven't created in this list, AstranS3ReadOnlyAccess
. That's because it's a builtin policy. You can identity a builtin policy by looking at its ARN, instead of an account Id, there's astran
.
Listing all versions of a policies
To list all versions of a policy run the following command (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam list-policy-versions --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy
Output:
{
"Versions": [
{
"VersionId": "v1",
"IsDefaultVersion": false,
"CreateDate": "2024-08-22T15:14:22.426880+00:00"
},
{
"VersionId": "v2",
"IsDefaultVersion": true,
"CreateDate": "2024-08-22T16:02:23.460863+00:00"
}
]
}
Checking the content policy
You can use the following command to check exactly what a policy does (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam get-policy-version --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy --version-id v1
Output:
{
"PolicyVersion": {
"Document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:*"],
"Resource": [
"arn:demo:s3:::marketing-docs",
"arn:demo:s3:::marketing-docs/*"
]
},
{
"Effect": "Allow",
"Action": ["s3:Get*", "s3:List*"],
"Resource": ["arn:demo:s3:::sales-docs", "arn:demo:s3:::sales-docs/*"]
}
]
},
"VersionId": "v1",
"IsDefaultVersion": false,
"CreateDate": "2024-08-22T15:14:22.426880+00:00"
}
}
Changing the default version of a policy
Let's say we changed our mind again and we decided that in the end we want the marketing
role to have a read only access to the sales-docs
bucket.
We can change the default version of the marketingPolicy
back to v1 with the following command (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran set-default-policy-version --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy --version-id v1
Which gives you no output, but if you do a get-policy
you will see that the default version changed (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran get-policy --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy
Output:
{
"Policy": {
"PolicyName": "marketingPolicy",
"PolicyId": "ANPA0D9040E6158F48FCA4389896C53EA2CF",
"Arn": "arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 1,
"CreateDate": "2024-08-22T15:14:22.426880+00:00",
"UpdateDate": "2024-08-22T16:27:59.766429+00:00"
}
}
Deleting a policy
To delete a policy, you first need to delete all versions that are not the default versions (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam delete-policy-version --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy --version-id v2
Then you need to detach the policy from all the roles it is attached to (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam detach-role-policy --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy --role-name marketing
Now you can delete the policy (as root):
# Replace the policy arn with your policy arn created in the first step
aws --profile astran iam delete-policy --policy-arn arn:demo:iam::ce04d61d-afac-504f-a96b-ebbbced80013:policy/marketingPolicy