Forbidden: ecs-task to apigateway websocket connection - amazon-web-services

I'm trying to make a post_to_connection request from an ecs-task to an APIGateway Websocket #connection api but was unable to do so from this ecs-task getting a Forbidden response every time.
This ecs-task has a role attached to it with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": "execute-api:ManageConnections",
"Resource": "arn:aws:execute-api:*:*:*"
}
]
}
Tests I've done:
I have copied the credentials found on this container; access_key_id, secret_access_key and the session and made the requests from my local machine which was successful.
I have created a credential object and passed that to the apigateway client and made the request from inside the container but it failed again with a Forbidden response.
To make these requests I'm using the same version of aws-sdk-apigatewaymanagementapi gem.
client = Aws::ApiGatewayManagementApi::Client.new(endpoint: url, region: 'eu-west-1')
resp = client.post_to_connection({data: "{type: 'hello'}",connection_id: "conn_id"})
At this point, I'm out of ideas as I don't have too much exp with AWS. Can you think of anything I could try?

Fixed this by creating a Custom Domain which was pointed to this ApiGateway instance.

Related

AWS IAM user credential always authenticated as anonymous

I am creating a simple API Gateway and trying to apply its auth. I created an IAM user (called postman-user) and created its credential (as AccessKeyId and SecretAccessKey).
My IAM User policy is like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "execute-api:*",
"Resource": "*"
}
]
}
and in my api gateway I applied the resource policy as below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<my account id>:root",
"arn:aws:iam::<my account id>:user/postman-user"
]
},
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-west-2:<my account id>:<my api g id>/*"
}
]
}
I applied the key id and secret key id in postman:
enter image description here
then the problem comes. no matter how I call the api endpoint using aws credential of this IAM user, I always got this error:
User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-west-2:******
I thought it was postman failed to sign this AWS sigV4, then I tried this in python:
url = 'https://<apig id>.execute-api.us-west-2.amazonaws.com/beta/query/'
auth = AWSRequestsAuth( aws_access_key='<my key id>',
aws_secret_access_key='<my secret key>',
aws_host='ec2.amazonaws.com',
aws_region='us-west-2',
aws_service='api')
response = requests.get(url, auth=auth)
This error is just forever for me
User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-west-2:******
Anyone can tell me what I missed ? I clicked on deployAPI in resource to stage beta 100 times ...
tried python, tried postman, nothing works
it sounds like there is something missing on the api plane. It may be the you havent configured IAM auth right on the http method you try to use. I may also be that the resource policy is not attached to the api gateway. Note if the policy is updated and reattached you need to redeploy the api gateway.
Link:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-create-attach.html
This is an API Gateway config issue:
Resources -> click on the method -> Method Request -> Authorization: it used to be None, changing to to AWS IAM made this work.

AWS Lambda | STS provided Access Token is invalid ("The security token included in the request is invalid", UnrecognizedClientException)

I'm creating a Lambda function written in Java 11 to authenticate Cognito Users from a certain User Pool. This function works well in local with my User's Access Key and Secret Key as I expect to receive a statusCode: 200 from the AdminInitiateAuth API call along with a body containing the user's valid JWTs.
However, when used in the Lambda Testing interface, the function returns a statusCode: 400 with an UnrecognizedClientException. Here is a stacktrace:
The security token included in the request is invalid. (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: UnrecognizedClientException; Request ID: 3cdf630c-bd5e-4370-9a18-cba4ef9439a9; Proxy: null): com.amazonaws.services.cognitoidp.model.AWSCognitoIdentityProviderException
com.amazonaws.services.cognitoidp.model.AWSCognitoIdentityProviderException: The security token included in the request is invalid. (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: UnrecognizedClientException; Request ID: 3cdf630c-bd5e-4370-9a18-cba4ef9439a9; Proxy: null)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1862)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleServiceErrorResponse(AmazonHttpClient.java:1415)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1384)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1154)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:811)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:779)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:753)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:713)
at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:695)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:559)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:539)
at com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClient.doInvoke(AWSCognitoIdentityProviderClient.java:8405)
at com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClient.invoke(AWSCognitoIdentityProviderClient.java:8372)
at com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClient.invoke(AWSCognitoIdentityProviderClient.java:8361)
at com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClient.executeAdminInitiateAuth(AWSCognitoIdentityProviderClient.java:1474)
at com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClient.adminInitiateAuth(AWSCognitoIdentityProviderClient.java:1443)
at fr.app.awscognito.awscognitospringauthserver.service.impl.CognitoUserServiceImpl.adminInitiateAuthResult(CognitoUserServiceImpl.java:243)
at fr.app.awscognito.awscognitospringauthserver.service.impl.CognitoUserServiceImpl.initiateAuth(CognitoUserServiceImpl.java:155)
at fr.app.awscognito.awscognitospringauthserver.service.impl.UserServiceImpl.authenticate(UserServiceImpl.java:34)
at fr.app.awscognito.awscognitospringauthserver.handler.AuthUserHandler.handleRequest(AuthUserHandler.java:87)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
I've checked the Lambda's Execution Role and it seems it has the requirements needed to mint a valid Access Key:
AWSLambdaBasicExecutionRole (redacted)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:eu-west-3:XXXXX:*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:eu-west-3:XXXXX:log-group:/aws/lambda/loginUser:*"
}
]
}
Role trusted entities
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
To resolve this issue, I've printed my Lambda's Environment variables keys (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) in my CloudWatch Logs and used them in an AWS CLI configured using these keys with the command aws sts get-caller-identity.
It returned the same error:
An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.
At this point I'm relatively clueless as this command is supposed to be permissionless, meaning that if the key does indeed exist, it should have returned me the details for this STS generated key.
I've also managed to find the related events in my CloudTrail logs, highlighting the fact that the key does indeed get minted twice from the AssumeRole call
The Decrypt call was used on the ASIAUK7PSYMR7OXYTZCN key only.
From my Lambda's CloudWatch Logs, here we find the first minted key (ASIAUK7PSYMRZRWUEUNJ) being used for the function.
Finally, I've tried these steps with an unrelated NodeJS function and had the same results (except AssumeRole gets called only once), the access key is not usable for any AWS Service.
How can I be able to use the access key in my Lambda function provided by STS ?

AWS API gateway giving 403 error while accessing from EC2 in Private Subnet

I have EC2 Instance running in private subnet and want to access API Gateway.
Steps i did:-
Created and Deployed stage in API gateway
Tested from test URL and postman - working fine
Created VPC Endpoint with execute-api interface
Updated API-Gateway resource policy as below and saved
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:eu-west-1:{{MY_ACCOUNT}}:{{MYAPI}}/*",
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpce-{{ID}}"
}
}
}
]
}
tried accessing from EC2 using curl and wget getting 403
[ec2-user#ip-xx-xx-xx-xx ~]$ wget https://{{MYAPI}}.execute-api.eu-west-1.amazonaws.com/DEV/CustomerInfo/?customerId={{CUST_ID}}
--2020-06-29 14:43:29-- https://{{MYAPI}}.execute-api.eu-west-1.amazonaws.com/DEV/CustomerInfo/?customerId={{CUST_ID}}
Resolving {{MYAPI}}.execute-api.eu-west-1.amazonaws.com ({{MYAPI}}.execute-api.eu-west-1.amazonaws.com)... xx.xx.xx.xx, xx.xx.xx.xxx
Connecting to {{MYAPI}}.execute-api.eu-west-1.amazonaws.com ({{MYAPI}}.execute-api.eu-west-1.amazonaws.com)|xx.xx.xx.xx|:443... connected.
HTTP request sent, awaiting response... 403 Forbidden
2020-06-29 14:43:29 ERROR 403: Forbidden.
Even tried this in VPC endpoint
Your condition:
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpce-{{ID}}"
}
}
does not seem right. StringNotEquals means that you allow execute-api:Invoke as long as the aws:sourceVpc is different than "vpce-{{ID}}.
I believe it should be StringEquals instead.
I was able to troubleshoot and resolve the issue.
Steps i did
Updated API Resource Policy condition to below
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-{{ID}}"
}
}
In my VPC endpoint Security Group updated Inbound Rule to accept HTTPS request on port 443
Saved and Redeployed API
Key takeaway was to redeploy API whenever you change anything in API.
It worked :)

AWS IoT MQTT over Websocket with STS temporary credentials

I am having issues using the temporary credentials to initiate a connection to AWS IoT using STS temporary credentials, whilst keeping things secure.
I have already successfully connected embedded devices using certificates with policies.
But when I come to try connecting via the browser, using a pre-signed URL, I have hit a stumbling block.
Below is a code snippet from a Lambda function which first authenticates the request (not shown), and then builds the url using STS credentials via assumeRole.
Using my generated URL along with Paho javascript client, I have been successful up to the point of receiving a response of "101 Switching Protocols" in the browser. But the connection is terminated instead of switching to websockets.
Any help or guidance anyone out there can provide me with would be much appreciated.
const iot = new AWS.Iot();
const sts = new AWS.STS({region: 'eu-west-1'});
const params = {
DurationSeconds: 3600,
ExternalId: displayId,
Policy: JSON.stringify(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:*"
],
"Resource": [
"*"
]
},
/*{
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": [
"arn:aws:iot:eu-west-1:ACCID:client/" + display._id
]
},
{
"Effect": "Allow",
"Action": [
"iot:Receive"
],
"Resource": [
"*"
]
}*/
]
}
),
RoleArn: "arn:aws:iam::ACCID:role/iot_websocket_url_role",
RoleSessionName: displayId + '-' + Date.now()
};
sts.assumeRole(params, function(err, stsData) {
if (err) {
fail(err, db);
return;
}
console.log(stsData);
const AWS_IOT_ENDPOINT_HOST = 'REDACTED.iot.eu-west-1.amazonaws.com';
var url = v4.createPresignedURL(
'GET',
AWS_IOT_ENDPOINT_HOST,
'/mqtt',
'iotdata',
crypto.createHash('sha256').update('', 'utf8').digest('hex'),
{
key: stsData.Credentials.AccessKeyId,
secret: stsData.Credentials.SecretAccessKey,
protocol: 'wss',
expires: 3600,
region: 'eu-west-1'
}
);
url += '&X-Amz-Security-Token=' + encodeURIComponent(stsData.Credentials.SessionToken);
console.log(url);
context.succeed({url: url});
});
Edit: If it helps, I just checked inside the "Frames" window in Chrome debugger, after selecting the request which returns a 101 code. It shows a single frame: "Binary Frame (Opcode 2, mask)".
Does this Opcode refer to MQTT control code 2 AKA "CONNACK"? I am not an expert at MQTT (yet!).
I realised my mistake by reading the docs on STS.
If you pass a policy to this operation, the temporary security credentials that are returned by the operation have the permissions that are allowed by both the access policy of the role that is being assumed, and the policy that you pass.
The RoleARN that is supplied must also allow the actions that you are requesting via STS assumeRole.
i.e. The RoleARN could allow iot:*, then when you assume role, you can narrow the permissions down to, for instance iot:Connect and for specific resources.

S3 putObject not working access denied

AWS S3 is working on my localhost and on my live website, but my development server (which is EXACTLY the same configuration) is throwing the following error: http://xxx.xx.xxx.xxx/latest/meta-data/iam/security-credentials/resulted in a404 Not Found` response: Error retrieving credentials from the instance profile metadata server.
Localhost URL is http://localhost/example
Live URL is http://www.example.com
Development URL is http://dev.example.com
Why would this work on localhost and live but not my development server?
Here is my sample code:
$bucket = 'example'
$s3Client = new S3Client([
'region'=>'us-west-2',
'version'=>'2006-03-01',
'key'=>'xxxxxxxxxxxxxxxxxxxxxxxx',
'secret'=>'xxxxxxxxxxxxxxxxxxxxxxxx',
]);
$uniqueFileName = uniqid().'.txt';
$s3Client->putObject([
'Bucket' => $bucket,
'Key' => 'dev/'.$uniqueFileName,
'Body' => 'this is the body!'
]);
Here is policy:
{
"Version": "2012-10-17",
"Id": "Policyxxxxxxxxx",
"Statement": [
{
"Sid": "Stmtxxxxxxxxx",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example/*"
}
]
}
The values returned by http://169.254.169.254/latest/meta-data/iam/security-credentials/ are associated with the Role assigned to the EC2 instance when the instance was first launched.
Since you are receiving a 404 Not Found response, it is likely that your Development server does not have a Role assigned. You can check in the EC2 management console -- just click on the instance, then look at the details pane and find the Role value.
If you wish to launch a new server that is "fully" identical, use the Launch More Like This command in the Actions menu. it will also copy the Role setting.