AWS Elastic Search Policy, only allow lambda to access Elastic Search - node.js

I'm working on setting up an ElasticSearch instance on AWS. My goal is to only allow http request from my Lambda function to the ElasticSearch instance. I have created one policy, that gives the 'Lambdaaccess to theElasticSearchinstance. The part I'm struggling with is the inline resource policy forElasticSearchthat will deny all other request that aren't from the 'Lambda.
I have tried setting the ElasticSearch resource policy to Deny all request and then giving my Lambda a role with access to ElasticSearch. While the Lambda is using that role I am signing my http requests using axios and aws4 but the request are rejected with The request signature we calculated does not match the signature you provided. I don't think the issue is the actual signing of the request but instead the polices I created. If anyone can steer me in the right direction that would really help.
Lambda Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"es:ESHttpGet",
"es:CreateElasticsearchDomain",
"es:DescribeElasticsearchDomainConfig",
"es:ListTags",
"es:ESHttpDelete",
"es:GetUpgradeHistory",
"es:AddTags",
"es:ESHttpHead",
"es:RemoveTags",
"es:DeleteElasticsearchDomain",
"es:DescribeElasticsearchDomain",
"es:UpgradeElasticsearchDomain",
"es:ESHttpPost",
"es:UpdateElasticsearchDomainConfig",
"es:GetUpgradeStatus",
"es:ESHttpPut"
],
"Resource": "arn:aws:es:us-east-1:,accountid>:domain/<es-instance>"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"es:PurchaseReservedElasticsearchInstance",
"es:DeleteElasticsearchServiceRole"
],
"Resource": "*"
}
]
}
ElasticSearch Inline Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"es:*"
],
"Resource": "arn:aws:es:us-east-1:<account-number>:domain/<es-instance>/*"
}
]
}
Lambda Code Using Aws4 and Axios
//process.env.HOST = search-<es-instance>-<es-id>.us-east-1.es.amazonaws.com
function createRecipesIndex(url, resolve, reject){
axios(aws4.sign({
host: process.env.HOST,
method: "PUT",
url: "https://" + process.env.HOST,
path: '/recipes/',
}))
.then(response => {
console.log("----- SUCCESS INDEX CREATED -----");
resolve();
})
.catch(error => {
console.log("----- FAILED TO CREATE INDEX -----");
console.log(error);
reject();
});
}
Note: I have tried creating my index with the inline policy on ElasticSearch set to allow *(all) and removing the aws4 library signature and it works fine. Right now I just want to secure access to this resource.

I found the solution to my issue and it was 2 fold. The first issue was my inline resource policy on my ElasticSearch instance. I needed to update it to allow the role that I have given to my Lambda. This was done by getting the role arn from IAM and then creating the below policy to be attached inline on the ElasticSearch instance.
My second issue was with aws4. the path and the url I set did not match. My path had /xxxx/ while my url was https://search-<es-instance>-<es-id>.us-east-1.es.amazonaws.com/xxxx. Since the path contained an extra forward slash not found in the url, the signing failed. For anyone else using the library make sure those values are consistent. I hope this helps someone else out in the future :D
Elastic Search Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account-id>:role/service-role/<role-name>"
},
"Action": "es:*",
"Resource": "arn:aws:es:us-east-1:<account-id>:domain/<es-instance>/*"
}
]
}

Related

Unable to resolve CORS error on API deployed on Elastic Beanstalk

I am working on a Node.js app deployment on Elastic beanstalk. Deployment goes well. EB Health is on OK status. Logs show server runs successfully. But when I run my frontend application deployed on S3, I get CORS error. I have set CORS on S3 as follows
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE"
],
"AllowedOrigins": [
"http://udagram-tauseef.s3-website-us-east-1.amazonaws.com"
],
"ExposeHeaders": [
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2"
],
"MaxAgeSeconds": 3000
}
]
And here's code for CORS in backend application.
const corsOptions = {
origin: '*',
optionsSuccessStatus: 200,
}
app.use(cors(corsOptions))
Bucket Policy that I'm using
{
"Version": "2012-10-17",
"Id": "BucketPolicy",
"Statement": [
{
"Sid": "AllAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mydemos3bucket",
"arn:aws:s3:::mydemos3bucket/*"
]
},
{
"Sid": "eb-ad78f54a-f239-4c90-adda-49e5f56cb51e",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::000246328708:role/aws-elasticbeanstalk-ec2-role"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::mydemos3bucket/resources/environments/logs/*"
},
{
"Sid": "eb-af163bf3-d27b-4712-b795-d1e33e331ca4",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::000246328708:role/aws-elasticbeanstalk-ec2-role"
},
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::mydemos3bucket",
"arn:aws:s3:::mydemos3bucket/resources/environments/*"
]
},
{
"Sid": "eb-58950a8c-feb6-11e2-89e0-0800277d041b",
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:DeleteBucket",
"Resource": "arn:aws:s3:::mydemos3bucket"
}
]
}
Also tried app.use(cors())
Here is the link to last EB Log file.
But still I am unable to get it resolved.
Here's an ss of error.
I have configured everything following documentation. But I'm stuck on this issue. Do I have to do something on EB? Or should I update CORS policy on S3 Bucket? Please let me know if I need to add more details to the question.
EDIT:
Here are network response headers on the API.
And here too, if you wanna see the OPTIONS header.
I think this is not a cors issue because you can see status code 502 means its bad gateway something is failing while you try to register operation.
if cors block your request won't hit your end-point so check your register method if there is any DB connection issue and are you sending a JSON response to the client
res.status(201).send({ message: 'Created' , response:data});
res.status(401).send({ "error":err});
you can also refer to this solution :
angular http retrieve data with post request
I think it is failing because your default cors options does not include options method.
Try following in your application: app.options('*', cors()). This will enable cors for preflight requests. You can read further about Why preflight happens,here.

RDS Proxy IAM role unable to retrieve credentials from secret

I am trying to implement a proxy to our Aurora RDS instance, but having difficulty getting the IAM access to work properly. We have a microservice in an ECS container that is attempting to access the database. The steps I've followed so far:
Created a secret containing the DB credentials
Created the proxy with the following config options:
Engine compatibility: MySQL
Require TLS - enabled
Idle timeout: 20 minutes
Secret - Selected DB credential secret
IAM Role - Chose to create new role
IAM Authentication - Required
Modified the policy of the proxy IAM role as per the details on this page.
Enabled enhanced logging
When issuing GET requests to the microservice, I see the following in the CloudWatch logs:
Credentials couldn't be retrieved. The IAM role "arn:our-proxy-role"
is not authorized to read the AWS Secrets Manager secret with the ARN
"arn:our-db-credential-secret"
Another interesting wrinkle to all of this: I pulled up the policy simulator, selecting the RDS proxy role and all of the actions under the Secrets Manager service, and all actions show up as being allowed.
I would sincerely appreciate any kind of guidance to indicate what I'm missing here.
arn:our-proxy-role
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds-db:connect"
],
"Resource": [
"arn:aws:rds:us-east-1:ACCOUNT:dbuser:*/*"
]
},
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"secretsmanager:GetRandomPassword",
"secretsmanager:CreateSecret",
"secretsmanager:ListSecrets"
],
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "secretsmanager:*",
"Resource": [
"arn:aws:our-db-credential-secret"
]
},
{
"Sid": "GetSecretValue",
"Action": [
"secretsmanager:GetSecretValue"
],
"Effect": "Allow",
"Resource": [
"arn:aws:our-db-credential-secret"
]
},
{
"Sid": "DecryptSecretValue",
"Action": [
"kms:Decrypt"
],
"Effect": "Allow",
"Resource": [
"arn:aws:kms:us-east-1:ACCOUNT:key/our-db-cluster"
],
"Condition": {
"StringEquals": {
"kms:ViaService": "secretsmanager.us-east-1.amazonaws.com"
}
}
}
]
}
The issue was related to security groups. I needed to specify an additional inbound rule to allow incoming traffic from itself so as to facilitate communication between resources that are part of the same security group.

Terraform v0.11 efs access permission denied

just want to ask if this has been a known issue on Terraform v0.11, I'm trying to mount efs to lambda however it seems being blocked on the part of querying the efs access point.
data.aws_efs_access_point.pogi: data.aws_efs_access_point.pogi: Error reading EFS access point : AccessDeniedException:
status code: 403, request id: 123k23s-1434-4421-as4ds-asd021390asdjj
my tf code below:
data "aws_efs_access_point" "pogi" {
access_point_id = "fsap-p0gigwap0h"
}
resource "aws_lambda_function" "pogi_function" {
function_name = "pogi-na-gwapo-pa"
...
file_system_config {
arn = "${data.aws_efs_access_point.pogi.arn}"
local_mount_path = "/mnt/pogi-mo"
}
}
NOTE: My tf code above is working when data source part is commented and arn value is hard coded
I'm using this IAM Role to deploy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt143441432",
"Action": [
"lambda:AddPermission",
"lambda:GetFunction",
"lambda:ListAliases",
"lambda:TagResource",
"lambda:UntagResource",
"lambda:UpdateFunctionConfiguration"
],
"Effect": "Allow",
"Resource": "arn:aws:lambda:us-east-1:1234567898765:function:pogi-*"
},
{
"Sid": "Stmt143441432",
"Action": [
"elasticfilesystem:Describe*",
"elasticfilesystem:List*"
],
"Effect": "Allow",
"Resource": "arn:aws:elasticfilesystem:us-east-1:1234567898765:file-system/*"
}
]
}

Download s3 files without using cli from specific IP sources

I have a requirement to download a file (as ex: https://hematestpolicy.s3.amazonaws.com/test/ca-dev2.png) s3 object across many instances in my aws vpc without having to install aws cli. The file should be protected, can be accessed only within VPC. I have applied below bucket policy on my s3 bucket hematestpolicy. Am able to view the file in my instances using aws s3 ls commands but unable to download it using wget command. Can anyone suggest if it is achievable or a better solution for file being private to vpc and downloaded without use of AWS CLI
`
{
"Version": "2012-10-17",
"Id": "CreditApplications",
"Statement": [
{
"Sid": "AllowCreditAppProcessing",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::975472539761:root",
"arn:aws:iam::975472539761:role/hema-ghh"
]
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::hematestpolicy",
"arn:aws:s3:::hematestpolicy/*"
],
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"172.31.0.0/16",
"192.168.2.6/16"
]
}
}
},
{
"Sid": "DenyEveryoneElse",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::hematestpolicy",
"arn:aws:s3:::hematestpolicy/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"172.31.0.0/16",
"192.168.2.6/16"
]
},
"ArnNotEquals": {
"aws:PrincipalArn": [
"arn:aws:iam::975472539761:role/hema-ghh",
"arn:aws:iam::975472539761:root"
]
}
}
}
]
}`
Unless you have a VPC endpoint all outgoing connections would be via a public source (for public instances this would be their public IP via an internet gateway, and for private this would be via a NAT).
If you want to limit to allow objects to only be retrieved via the VPC you should look at using a VPC endpoint for S3. By creating this and adding it to your route tables it will actually also provide a internal connection to S3 vs using the public internet.
Once you have this in place you can create a bucket policy that completely limits the requests to the source of that VPC endpoint.
For example the below policy would Deny access where not from the VPC Endpoint.
{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Principal": "*",
"Action": "s3:*",
"Effect": "Deny",
"Resource": ["arn:aws:s3:::awsexamplebucket1",
"arn:aws:s3:::awsexamplebucket1/*"],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-1a2b3c4d"
}
}
}
]
}
Be aware that when using a bucket policy denying everything will restrict all access to that bucket (including management tasks) to only be available from that source VPC endpoint, so you should try to limit the scope of actions i.e. GetObject.

AWS Video Rekognition is not publishing results to SNS Topic

Running some nodejs aws rekognition to detect labels in mp4 video, but it will not publish to the specified SNS topic when complete. I don't get any permission errors when submitting the request with the topic/ROLE arns.
const AWS = require('aws-sdk');
AWS.config.update(
{
region: 'us-west-2',
accessKeyId: "asdfadsf",
secretAccessKey: "asdfasdfasdfasd1234123423"
}
);
const params = {
Video: {
S3Object: {
Bucket: 'myvidebucket',
Name: '5d683b81760ec59c2015.mp4'
}
},
NotificationChannel: {
RoleArn: 'arn:aws:iam::xxxxxxxxxxxxx:role/AmazonRekognitionSNSSuccessFeedback',
SNSTopicArn: 'arn:aws:sns:us-west-2:xxxxxxxxxxxxx:recoknize',
},
MinConfidence: 60
};
rekognition.startLabelDetection(params).promise().then(data => {
console.log(JSON.stringify(data));
}).catch(error => {
console.log(error);
});
That code executes with no errors, and I get back a job id. My SNS topic subscription is confirmed, and supposed to post to my HTTPS endpoint. But nothing ever arrives, and there are no error logs anywhere in AWS console about this.
When I manually access the rekogniztion by jobid, the data comes back fine so I know it finished correctly. Something strange has to be going on with IAM permissions.
I have reviewed and tested your nodejs code successfully and I don't see anything wrong with it.
Since, the code returns the AWS Rekognition "JobId" successfully, you can review your SNS configuration and check if it matches the following:
1. On your SNS topic ('arn:aws:sns:us-west-2:xxxxxxxxxxxxx:recoknize'), navigate to the access policy and check if you have a policy similar to the following :
{
"Version": "2008-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__default_statement_ID",
"Effect": "Allow",
"Principal": {
"Service": "rekognition.amazonaws.com"
},
"Action": [
"SNS:GetTopicAttributes",
"SNS:SetTopicAttributes",
"SNS:AddPermission",
"SNS:RemovePermission",
"SNS:DeleteTopic",
"SNS:Subscribe",
"SNS:ListSubscriptionsByTopic",
"SNS:Publish",
"SNS:Receive"
],
"Resource": "arn:aws:sns:us-west-2:XXXXXXXXXXXX:AmazonRekognitionTopic"
}
]
}
2. On your IAM role ('arn:aws:iam::xxxxxxxxxxxxx:role/AmazonRekognitionSNSSuccessFeedback'), make sure of the following:
(i) The "Trust relationship" of your role has the following statement :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service":"rekognition.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
(ii) The role has an attached policy document similar to one given below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sns:publish"
],
"Resource": "*"
}
]
}
The successful published message from Amazon Rekognition to SNS topic should output something similar to:
"JobId":"8acd9edd6edfb0e4985f8cd269e4863e54f7fcd451af6aafe10b32996dedbdba","Status":"SUCCEEDED","API":"StartLabelDetection","Timestamp":1568544553927,"Video":{"S3ObjectName":"final.mp4","S3Bucket":"syumak-rekognition"}}
Hope this helps.
Buried in the docs - it's apparent that
https://docs.aws.amazon.com/rekognition/latest/dg/api-video-roles.html#api-video-roles-all-topics
AmazonRekognitionServiceRole gives Amazon Rekognition Video access to
Amazon SNS TOPICS that are PREFIXED with AmazonRekognition.
It doesn't say the role ARN needs to be prefixed. But won't hurt.
Double check your TOPIC is AmazonRekognitionMyTopicName
RoleArn: 'arn:aws:iam::xxxxxxxxxxxxx:role/AmazonRekognitionSNSSuccessFeedback', <- don't think this is so important.
SNSTopicArn: 'arn:aws:sns:us-west-2:xxxxxxxxxxxxx:recoknize', <- Must be something like AmazonRekognitionSuccess
Also - this helped / I moved off the FIFO which allows subscribing via email in addition to SQS.
https://docs.aws.amazon.com/rekognition/latest/dg/video-troubleshooting.html
This line
Verify that you have an IAM service role that gives Amazon Rekognition Video permissions to publish to your Amazon SNS topics. For more information, see Configuring Amazon Rekognition Video.
I created a new IAM and gave it
AmazonRekognitionFullAccess
AmazonSNSRole
AmazonSNSFullAccess
I updated the trust relationship to include both sns.amazonaws.com /
rekognition.amazonaws.com.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"sns.amazonaws.com",
"rekognition.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
Not sure which one of these made everything click - but was a good half day on this / hopefully this will save someone some time.
Trust relationship solved it for me. Add the below script to the trust relationship of the IAM that will be used as RoleARn for the script:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": [
"sns.amazonaws.com",
"rekognition.amazonaws.com",
"sagemaker.amazonaws.com"
]
},
"Action": "sts:AssumeRole",
"Condition": {}
}]
}

Resources