简体   繁体   中英

API Gateway cross account access

I have two AWS accounts, consider for example account-a and account-b. account-a wants to access an API deployed on API gateway in account-b. Now am able to access the API in account-b from account-a. I want to add white listing for the API to allow only account-a. When I added a resource policy, white-listing only account-a for the API , it always throws an error "user anonymous is not allowed". Can someone let me know whether am following the right approach. Please let me know if I missed anything.

I added auth as "AWS_IAM" and had the following resource policy.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::****:role/****"
            },
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:ap-southeast-1:****:***/*/*/*"
        }
    ]
}

Now am getting the error

"Missing authentication token"

.

My requirement is to secure the API deployed in AWS API gateway with either aws accounts whitelisting or source-vpc filtering. Please help to provide the required info as well as if any special handling while generating the api request(like signing the request). A simple example would be greatly appreciated.

This error is caused because at the client side, you need to sign your request with an AWS authentication header. Normally for calls to aws services, when you use an AWS sdk, it automatically signs the request. Unfortunately there's no official amazon way to sign this request for invoking the API as of now as you can see here . You can use third party libraries like aws-requests-auth in the following way:

from aws_requests_auth.boto_utils import BotoAWSRequestsAuth
auth = BotoAWSRequestsAuth(aws_host='api.example.com',
                           aws_region='us-east-1',
                           aws_service='execute-api')

import requests
response = requests.post('https://api.example.com/test', json={"foo": "bar"}, auth=auth)

If you're looking to utilize this in a lambda function, you can add this as a layer, by installing it to a target folder, and then zipping the folder and using it as a lambda layer.

As @Sam mentioned, you need to generate a signature and send it with your http request. You can do this in two ways.

  1. You can generate the signature and send the signature with your http request as headers
  2. you can use a library that can generate the signature and send the request

Method 1 - generate signature

### generate signature
const aws4  = require('aws4')
const signature = aws4.sign({
  host: 'https://apiId.execute-api.ap-southeast-2.amzonaws.com',
  method: 'GET',
  path: '/development/hello',
  headers: {
  
  },
  region: 'ap-southeast-2',
  service: 'execute-api'
}, {
  secretAccessKey: "your access key",
  accessKeyId: "your secret key",
  sessionToken: "your session token if you are using temporary credentials"

})

// output
{ "host":
   "something.execute-api.ap-southeast-2.amzonaws.com",
  "method": "GET",
  "path": "/development/hello",
  "headers": {
     "Host":
      "something.execute-api.ap-southeast-2.amzonaws.com",
     "X-Amz-Security-Token":
      "security token",
     "Authorization":
      "AWS4-HMAC-SHA256 Credential=ASIARNZFFFFFEGFG23JY/20191212/ap-southeast-2/execute-api/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token;x-apigw-api-id, Signature=7fd8e51c2bf4faefaRRRRRf92c700799b78234d204"
  },
  "region": "ap-southeast-2",
  "service": "execute-api"
}

Include Authorization , X-Amz-Date and X-Amz-Security-Token as the headers with your http request.

Method 2 - use a library that can generate the signature and send the request

var apigClientFactory = require('aws-api-gateway-client').default;

var apigClient = apigClientFactory.newClient({
    invokeUrl:'https://apiId.execute-api.ap-southeast-2.amzonaws.com/development', // REQUIRED
    accessKey: 'your access key', // REQUIRED
    secretKey: 'your secret key', // REQUIRED
    sessionToken: 'your session token if you are using temporary credentials',
    region: 'ap-southeast-2', // REQUIRED: The region where the API is deployed.
    systemClockOffset: 0, // OPTIONAL: An offset value in milliseconds to apply to signing time
    retries: 4, // OPTIONAL: Number of times to retry before failing. Uses axon-retry plugin.
    retryCondition: (err) => { // OPTIONAL: Callback to further control if request should be retried.  Uses axon-retry plugin.
      return err.response && err.response.status === 500;
    }
});


(() => {
  apigClient.invokeApi(null, `/hello`, 'GET', {
    headers: {
    }
  })
  .then(function(result){
    console.log('result: ', result)
      //This is where you would put a success callback
  }).catch( function(result){
    console.log('result: ', result)
      //This is where you would put an error callback
  });
})()

Hope this helps, good luck

You will have to generate a signed request. Below is the Python code that you can run locally

import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import requests

session = boto3.Session(profile_name="YOUR PROFILE NAME")
credentials = session.get_credentials()
creds = credentials.get_frozen_credentials()


def signed_request(method, url, data=None, params=None, headers=None):
    request = AWSRequest(method=method, url=url, data=data, params=params, headers=headers)
    SigV4Auth(creds, "YOUR API NAME", "us-east-1").add_auth(request)
    return requests.request(method=method, url=url, headers=dict(request.headers), data=data)


def main():
    url = f"YOUR API URL"
    data = '{"data":"John"}'
    headers = {'Content-Type': 'application/json'}
    response = signed_request(method='POST', url=url, data=data, headers=headers)
    print(response.text)


if __name__ == "__main__":
    main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM