Skip to main content

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:

  1. The second statement has now a Effect: "Deny", which means it's an explicit Deny.
  2. 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. Policy evaluation

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"
}
}
caution

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