How to limit SSM based on user starting the command - amazon-web-services

I have an EC2 that I am connecting to using the AWS Systems manager, the EC2 has a role of AmazonSSMManagedInstanceCore attached and I am able to use ssm startSession from the CLI.
Without adding permissions to the users themselves am I able to limit which users are allowed to initiate a session to the EC2s?
I have tried adding a second policy to the EC2s where I block access to ssm:StartSession (which works when I apply it with no condition) with a condition containing aws.userid and aws:ssmmessages:session-id but neither of these blocked access.
I am using federated users in this account.
Below is an example of the most recent policy attempting to block access to that specific email but not others (which does not work).
const myPolicy = new ManagedPolicy(this, "sendAndBlockPoicy", {
statements: [
new PolicyStatement({
sid: "AllowSendCommand",
effect: Effect.ALLOW,
resources: [`arn:aws:ec2:${Aws.REGION}:${Aws.ACCOUNT_ID}:*`],
actions: ["ssm:SendCommand"],
}),
new PolicyStatement({
sid: "blockUsers",
effect: Effect.DENY,
resources: ["*"],
actions: ["ssm:*", "ssmmessages:*", "ec2messages:*"],
conditions: {
StringLike: {
"aws:ssmmessages:session-id":
"ABCDEFGHIJKLMNOPQRSTUV:me#email.com",
},
},
}),
],
});
const managedSSMPolicy = ManagedPolicy.fromAwsManagedPolicyName(
"AmazonSSMManagedInstanceCore",
);
const role = new Role(this, 'ec2Role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com')
managedPolicies: [managedSSMPolicy, myPolicy ]
}

Related

AWS CDK grant decrypt permission to Kinesis Data Stream's AWS managed CMK

I'm provisioning Kinesis Data Stream with AWS managed KMS key as well as Delivery Stream reading from stream. There's a problem on how to add decrypt policy on delivery stream role for managed key. The code is showing below and the issue is that getting key with 'aws/kinesis' alias doesn't work unless I have a way to add dependency to 'kinesisStream' resource. But there's no 'addDependsOn' method in IKey-interface. How can I ensure that Stream (and it's managed KMS key) is created before I try to fetch that key?
const kinesisStream = new kinesis.Stream(this, 'kinesisStream', {
streamName: `my-stream`,
shardCount: 1,
encryption: kinesis.StreamEncryption.MANAGED,
retentionPeriod: cdk.Duration.days(1),
});
const kinesisStreamRole = new iam.Role(this, 'kinesisStreamRole', {
assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'),
});
// How to add dependency to kinesisStream resource to ensure it's created before trying to fetch KMS key using 'fromLookup'?
// Now getting:
// [Error at /my-stack] Could not find any key with alias named aws/kinesis
const managedKinesisKmsKey = kms.Key.fromLookup(this, 'managedKinesisKmsKey', {
aliasName: 'aws/kinesis',
});
const managedKinesisKmsKeyPolicy = new iam.Policy(this, 'managedKinesisKmsKeyPolicy', {
roles: [kinesisStreamRole],
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: [managedKinesisKmsKey.keyArn],
actions: ['kms:Decrypt'],
}),
],
});
You can use the key alias to grant the access to this AWS managed key. We know that the alias for Kinesis service specific AWS managed key is "aws/kinesis".
AWS developer guide for using aliases to control access to KMS keys: https://docs.aws.amazon.com/kms/latest/developerguide/alias-authorization.html
Working solution
const kinesisStream = new kinesis.Stream(this, 'kinesisStream', {
streamName: `my-stream`,
shardCount: 1,
encryption: kinesis.StreamEncryption.MANAGED,
retentionPeriod: cdk.Duration.days(1),
});
const kinesisStreamRole = new iam.Role(this, 'kinesisStreamRole', {
assumedBy: new iam.ServicePrincipal('firehose.amazonaws.com'),
});
const managedKinesisKmsKeyPolicy = new iam.Policy(this, 'managedKinesisKmsKeyPolicy', {
roles: [kinesisStreamRole],
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
resources: ['*'],
actions: ['kms:Decrypt'],
conditions: {
StringLike: {
'kms:RequestAlias': 'aws/kinesis',
},
},
}),
],
});

Giving access to everything within S3 bucket

Does anyone know if I can use a wildcard and give access to everything within S3 bucket?
Instead of adding every location explicitly like I am currently doing?
const policyDoc = new PolicyDocument({
statements: [
new PolicyStatement({
sid: 'Grant role to read/write to S3 bucket',
resources: [
`${this.attrArn}`,
`${this.attrArn}/*`,
`${this.attrArn}/emailstore`,
`${this.attrArn}/emailstore/*`,
`${this.attrArn}/attachments`,
`${this.attrArn}/attachments/*`
],
actions: ['s3:*'],
effect: Effect.ALLOW,
principals: props.allowedArnPrincipals
})
]
});
You should be able to use:
resources: [
`${this.attrArn}`,
`${this.attrArn}/*`
],
The first one gives permission for actions on the bucket itself (eg ListBucket), while /* gives permission for actions inside the bucket (eg GetObject).

A PolicyStatement used in an identity-based policy cannot specify any IAM principals error

let servicePrincipal: any = new iam.ServicePrincipal("lambda.amazonaws.com");
let policyDoc = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: ["sts:AssumeRole"],
principals: [servicePrincipal],
effect: iam.Effect.ALLOW,
resources: ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],
sid: ""
})
],
});
let accessRole: any = new iam.Role(this, 'git-access-role', {
assumedBy: servicePrincipal,
inlinePolicies: { policyDoc }
});
I'm creating a cdk lambda with a role that has AWSLambdaBasicExecutionRole but I get an error saying
A PolicyStatement used in an identity-based policy cannot specify any IAM principals
not quite sure...what does it mean and what should I do?
Looks like you're trying to generate the assume role policy with policyDoc. The assumedBy: servicePrincipal line will automatically generate the trust policy. If all you want to do is assign the lambda basic execution policy to the role, then it should look like this:
const accessRole = new iam.Role(this, 'git-access-role', {
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')]
});
If the lambda needs access to git as the construct id of the role seems to indicate then you can add those permissions as inline policies. But this code would create a role that is assumable by a lambda and it would have the most basic permissions a lambda needs to run.

AWS Textract StartDocumentAnalysis function not publishing a message to the SNS Topic

I am working with AWS Textract and I want to analyze a multipage document, therefore I have to use the async options, so I first used startDocumentAnalysisfunction and I got a JobId as the return, But it needs to trigger a function that I have set to trigger when the SNS topic got a message.
These are my serverless file and handler file.
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:*"
Resource: { "Fn::Join": ["", ["arn:aws:s3:::${self:custom.secrets.IMAGE_BUCKET_NAME}", "/*" ] ] }
- Effect: "Allow"
Action:
- "sts:AssumeRole"
- "SNS:Publish"
- "lambda:InvokeFunction"
- "textract:DetectDocumentText"
- "textract:AnalyzeDocument"
- "textract:StartDocumentAnalysis"
- "textract:GetDocumentAnalysis"
Resource: "*"
custom:
secrets: ${file(secrets.${opt:stage, self:provider.stage}.yml)}
functions:
routes:
handler: src/functions/routes/handler.run
events:
- s3:
bucket: ${self:custom.secrets.IMAGE_BUCKET_NAME}
event: s3:ObjectCreated:*
textract:
handler: src/functions/routes/handler.detectTextAnalysis
events:
- sns: "TextractTopic"
resources:
Resources:
TextractTopic:
Type: AWS::SNS::Topic
Properties:
DisplayName: "Start Textract API Response"
TopicName: TextractResponseTopic
Handler.js
module.exports.run = async (event) => {
const uploadedBucket = event.Records[0].s3.bucket.name;
const uploadedObjetct = event.Records[0].s3.object.key;
var params = {
DocumentLocation: {
S3Object: {
Bucket: uploadedBucket,
Name: uploadedObjetct
}
},
FeatureTypes: [
"TABLES",
"FORMS"
],
NotificationChannel: {
RoleArn: 'arn:aws:iam::<accont-id>:role/qvalia-ocr-solution-dev-us-east-1-lambdaRole',
SNSTopicArn: 'arn:aws:sns:us-east-1:<accont-id>:TextractTopic'
}
};
let textractOutput = await new Promise((resolve, reject) => {
textract.startDocumentAnalysis(params, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
I manually published an sns message to the topic and then it is firing the textract lambda, which currently has this,
module.exports.detectTextAnalysis = async (event) => {
console.log('SNS Topic isssss Generated');
console.log(event.Records[0].Sns.Message);
};
What is the mistake that I have and why the textract startDocumentAnalysis is not publishing a message and making it trigger the lambda?
Note: I haven't use the startDocumentTextDetection before using the startTextAnalysis function, though it is not necessary to call it before this.
Make sure you have in your Trusted Relationships of the role you are using:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"textract.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
The SNS Topic name must be AmazonTextract
At the end your arn should look this:
arn:aws:sns:us-east-2:111111111111:AmazonTextract
I was able got this working directly via Serverless Framework by adding a Lambda execution resource to my serverless.yml file:
resources:
Resources:
IamRoleLambdaExecution:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- textract.amazonaws.com
Action: sts:AssumeRole
And then I just used the same role generated by Serverless (for the lambda function) as the notification channel role parameter when starting the Textract document analysis:
Thanks to this this post for pointing me in the right direction!
For anyone using the CDK in TypeScript, you will need to add Lambda as a ServicePrincipal as usual to the Lambda Execution Role. Next, access the assumeRolePolicy of the execution role and call the addStatements method.
The basic execution role without any additional statement (add those later)
this.executionRole = new iam.Role(this, 'ExecutionRole', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
});
Next, add Textract as an additional ServicePrincipal
this.executionRole.assumeRolePolicy?.addStatements(
new PolicyStatement({
principals: [
new ServicePrincipal('textract.amazonaws.com'),
],
actions: ['sts:AssumeRole']
})
);
Also, ensure the execution role has full permissions on the target SNS topic (note the topic is created already and accessed via fromTopicArn method)
const stmtSNSOps = new PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"SNS:*"
],
resources: [
this.textractJobStatusTopic.topicArn
]
});
Add the policy statement to a global policy (within the active stack)
this.standardPolicy = new iam.Policy(this, 'Policy', {
statements: [
...
stmtSNSOps,
...
]
});
Finally, attach the policy to the execution role
this.executionRole.attachInlinePolicy(this.standardPolicy);
If you have your bucket encrypted you should grant kms permissions, otherwise it won't work

ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC

I am trying to create a VPC controlled Elastic Search Service on AWS. The problem is I keep getting the error when I run the following code: 'ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC'.
const AWS = require('aws-sdk');
AWS.config.update({region:'<aws-datacenter>'});
const accessPolicies = {
Statement: [{
Effect: "Allow",
Principal: {
AWS: "*"
},
Action: "es:*",
Resource: "arn:aws:es:<dc>:<accountid>:domain/<domain-name/*"
}]
};
const params = {
DomainName: '<domain>',
/* required */
AccessPolicies: JSON.stringify(accessPolicies),
AdvancedOptions: {
EBSEnabled: "true",
VolumeType: "io1",
VolumeSize: "100",
Iops: "1000"
},
EBSOptions: {
EBSEnabled: true,
Iops: 1000,
VolumeSize: 100,
VolumeType: "io1"
},
ElasticsearchClusterConfig: {
DedicatedMasterCount: 3,
DedicatedMasterEnabled: true,
DedicatedMasterType: "m4.large.elasticsearch",
InstanceCount: 2,
InstanceType: 'm4.xlarge.elasticsearch',
ZoneAwarenessEnabled: true
},
ElasticsearchVersion: '5.5',
SnapshotOptions: {
AutomatedSnapshotStartHour: 3
},
VPCOptions: {
SubnetIds: [
'<redacted>',
'<redacted>'
],
SecurityGroupIds: [
'<redacted>'
]
}
};
const es = new AWS.ES();
es.createElasticsearchDomain(params, function (err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
console.log(JSON.stringify(data, null, 4)); // successful response
}
});
The problem is I get this error: ValidationException: Before you can proceed, you must enable a service-linked role to give Amazon ES permissions to access your VPC. I cannot seem to figure out how to create this service linked role for the elastic search service. In the aws.amazon.com IAM console I cannot select that service for a role. I believe it is supposed to be created automatically.
Has anybody ran into this or know the way to fix it?
The service-linked role can be created using the AWS CLI.
aws iam create-service-linked-role --aws-service-name opensearchservice.amazonaws.com
Previous answer: before the service was renamed, you would do the following:
aws iam create-service-linked-role --aws-service-name es.amazonaws.com
You can now create a service-linked role in a CloudFormation template, similar to the Terraform answer by #htaccess. See the documentation for the CloudFormation syntax for Service-Linked Roles for more details
YourRoleNameHere:
Type: 'AWS::IAM::ServiceLinkedRole'
Properties:
AWSServiceName: es.amazonaws.com
Description: 'Role for ES to access resources in my VPC'
For terraform users who hit this error, you can use the aws_iam_service_linked_role resource to create a service linked role for the ES service:
resource "aws_iam_service_linked_role" "es" {
aws_service_name = "es.amazonaws.com"
description = "Allows Amazon ES to manage AWS resources for a domain on your behalf."
}
This resource was added in Release 1.15.0 (April 18, 2018) of the AWS Provider.
Creating a elasticsearch domain with VPC and using aws-sdk/cloudformation is currently not supported. The elasticsearch service requires a special service linked role to create the network interfaces in the specified VPC. This currently possible using console / cli(#Oscar Barrett's answer below).
However, there is a workaround to get this working and it is described as follows:
Create a test elasticsearch domain with VPC access using console.
This will create a service linked role named AWSServiceRoleForAmazonElasticsearchService [Note: You can not create the role with specified name manually or through thr console]
Once this role is created, use aws-sdk or cloudformation to create elasticsearch domain with VPC.
You can delete the test elasticsearch domain later
Update: The more correct way to create the service role is described in #Oscar Barrett's answer. I was thinking to delete my answer; but the other facts about the actual issue is still more relevant, thus keeping my answer here.
Do it yourself in CDK:
const serviceLinkedRole = new cdk.CfnResource(this, "es-service-linked-role", {
type: "AWS::IAM::ServiceLinkedRole",
properties: {
AWSServiceName: "es.amazonaws.com",
Description: "Role for ES to access resources in my VPC"
}
});
const esDomain = new es.CfnDomain(this, "es", { ... });
esDomain.node.addDependency(serviceLinkedRole);