Reputation: 13
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.
Upvotes: 0
Views: 10364
Reputation: 200
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()
Upvotes: 1
Reputation: 8583
As @Sam mentioned, you need to generate a signature and send it with your http request. You can do this in two ways.
### 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.
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
Upvotes: 0
Reputation: 195
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.
Upvotes: 0