Chalice: Bug: `chalice deploy` fails to discover required permissions for Lambda IAM role

Created on 24 Jan 2018  路  10Comments  路  Source: aws/chalice

Ordinarily, chalice deploy finds IAM permissions required for the application to work, builds an IAM role and prompts the user for authorization to add necessary permissions to the role:

$ chalice deploy
Regen deployment package.

The following actions will be added to the execution policy:

s3:GetObject

Would you like to continue?  [Y/n]:

However, in some situations, the required permissions are not discovered and the app is deployed without the required permissions, resulting in botocore.exceptions.ClientError (AccessDenied) when a request is received.

In the example below, the app is deployed with a role that is missing the permissions that it requires.

MVCE

app.py:

import boto3
from chalice import Chalice

app = Chalice(app_name='bug')
s3 = boto3.client('s3')
app.debug = True


@app.route('/')
def index():
    data = get_content('foo')
    return {'data': data.decode()}


def get_content(key):
    content = s3.get_object(Bucket='this-chalice-resembles-a-grail',
                            Key=key)['Body'].read()
    return content
bug

Most helpful comment

We had the same issue with Chalice. It didn't discover all required permissions. We spent a fair amount of time testing different ideas and suggestions. We were not able to get Chalice to cover all permissions. And also the manual policy creation is not a viable option for our purpose.

Finally, I found a workaround that did the trick for us. But be warned: it is very ugly :)

We handpicked all the boto3.client('s3') functions we need/call in the API.
And created a dummy function like

def dummy():
    """
    Collection of all s3.client() functions.
    The sole purpose is to force Chalice to generate the right permissions in the policy.
    Does nothing and returns nothing.
    """
    s3 = boto3.client('s3')
    s3.put_object()
    s3.download_file()
    s3.get_object()
    s3.list_objects_v2()
    s3.get_bucket_location()

Then "call" the dummy in the root route, and make sure it will never be called, like:

@app.route('/')
def index():
    if False:     # make this to never evaluate as True
        dummy()
    ...

Without changing any settings in the config, all required permission magically appeared in the autogenerated policy after deploy.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:PutObject"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "..."
    }
  ]
}

All 10 comments

I can confirm as well. I was even just doing this:

from chalice import Chalice
import boto3

app = Chalice(app_name='pycascades')
ec2 = boto3.client('ec2')


@app.route('/')
def index():
    return get_regions()


def get_regions():
    return ec2.describe_regions()

I get prompted correctly for policy generation for this though:

from chalice import Chalice
import boto3

app = Chalice(app_name='pycascades')
ec2 = boto3.client('ec2')


def get_regions():
    return ec2.describe_regions()


@app.route('/')
def index():
    return get_regions()

It looks like if the function gets defined before the index() function, it fails to traverse through the get_regions() function as it has yet to have the AST node registered to the get_regions symbol.

It looks like s3.upload_file is not detected at all even if it's in index(). Is there a workaround in the meantime? Because as it is now, even when I edit the policy manually, the next deployment will prompt me to overwrite it and there doesn't seem to be a way to skip the overwrite.

@weilu You can specify a role manually and tell Chalice to stop overwriting it.

Here's an example that may help with editing your .chalice/config.json file:

{
  "version": "2.0",
  "app_name": "example-app",
  "manage_iam_role": false,
  "iam_role_arn": "arn:aws:iam::424242424242:role/service-role/S3_read_access",
  "stages": {
    "dev": {
      "api_gateway_stage": "api"
    }
  }
}

Replace 424242424242 with your account number.
Replace S3_read_access with your service role name.
Replace example-app with your app name.

When manage_iam_role is set to false, Chalice will not auto-generate a new role.

We had the same issue with Chalice. It didn't discover all required permissions. We spent a fair amount of time testing different ideas and suggestions. We were not able to get Chalice to cover all permissions. And also the manual policy creation is not a viable option for our purpose.

Finally, I found a workaround that did the trick for us. But be warned: it is very ugly :)

We handpicked all the boto3.client('s3') functions we need/call in the API.
And created a dummy function like

def dummy():
    """
    Collection of all s3.client() functions.
    The sole purpose is to force Chalice to generate the right permissions in the policy.
    Does nothing and returns nothing.
    """
    s3 = boto3.client('s3')
    s3.put_object()
    s3.download_file()
    s3.get_object()
    s3.list_objects_v2()
    s3.get_bucket_location()

Then "call" the dummy in the root route, and make sure it will never be called, like:

@app.route('/')
def index():
    if False:     # make this to never evaluate as True
        dummy()
    ...

Without changing any settings in the config, all required permission magically appeared in the autogenerated policy after deploy.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:PutObject"
      ],
      "Resource": [
        "*"
      ],
      "Sid": "..."
    }
  ]
}

@weilu You can specify a role manually and tell Chalice to stop overwriting it.

Here's an example that may help with editing your .chalice/config.json file:

{
  "version": "2.0",
  "app_name": "example-app",
  "manage_iam_role": false,
  "iam_role_arn": "arn:aws:iam::424242424242:role/service-role/S3_read_access",
  "stages": {
    "dev": {
      "api_gateway_stage": "api"
    }
  }
}

Replace 424242424242 with your account number.
Replace S3_read_access with your service role name.
Replace example-app with your app name.

When manage_iam_role is set to false, Chalice will not auto-generate a new role.

This role has to be a different role though, right? Saying I want to keep the current role but just stop updating it is not an option. When I set the this in my config, Chalice fails to deploy because it cannot delete the current role (the one I want to keep but not update). Of course it cannot delete the role because there are policies attached to it.

Hello. It's been over 2 years since the bug was reported. Do you have any ETA for the fix or do we still need to use workarounds?

Bumping this. We have a scenario where we are configuring a few different things when the app starts up, including fetching the name of a resource. The endpoint can't seem to figure out that it needs the access to do that....

Still seem to be running into this issue with chalice 1.22.2, python 3.6.9, linux 4.15.0-136-generic. Don't have a code snipped ready to publish, but chalice suddenly stopped detecting my s3 upload_file() call through boto3... Not sure why, it also seems to fail even on previous commits where it had been working. Seems like there is a cache somewhere I need to clear? Tried deleting old deployments, removing __pycache__ dir, still seems to fail..

Seems like chalice won't detect s3 permissions if you use a factory method with a global variable for injecting a stub, like in the last example here: https://aws.amazon.com/blogs/developer/introducing-the-new-test-client-for-aws-chalice/.

Was this page helpful?
0 / 5 - 0 ratings