I am currently trying to create an aggregator for all of the config rules I created in order for a client to have a centralized place to view all regions config metrics.
Here is my code to create the configAggregator:
//adding role for configAggregator
const configAggregatorRole = new iam.Role(this, 'configAggregatorRole' ,{
assumedBy: new iam.ServicePrincipal('config.amazonaws.com')
});
configAggregatorRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSConfigRoleforOrganizations'));
configAggregatorRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('ReadOnlyAccess'));
//adding a content aggregator for managed config rules below
const globalConfigAggregator = new config.CfnConfigurationAggregator(this, 'globalConfigAggregator',{
configurationAggregatorName: 'globalConfigAggregator',
AccountAggregationSourceProperty: {
accountIds : this.account
}
});
}
}
I am currently trying to figure out what I should pass to specify that I want this account and x region to be the aggregated view of all the config rules in all the regions in that account. I am not sure how to do this. Thank you!
Related
I want to save an initial admin user to my dynamodb table when initializing a cdk stack through a custom resource and am unsure of the best way to securely pass through values for that user. My code uses dotEnv and passes the values as environment variables right now:
import * as cdk from "#aws-cdk/core";
import * as lambda from "#aws-cdk/aws-lambda";
import * as dynamodb from "#aws-cdk/aws-dynamodb";
import * as customResource from "#aws-cdk/custom-resources";
require("dotenv").config();
export class CDKBackend extends cdk.Construct {
public readonly handler: lambda.Function;
constructor(scope: cdk.Construct, id: string) {
super(scope, id);
const tableName = "CDKBackendTable";
// not shown here but also:
// creates a dynamodb table for tableName and a seedData lambda with access to it
// also some lambdas for CRUD operations and an apiGateway.RestApi for them
const seedDataProvider = new customResource.Provider(this, "seedDataProvider", {
onEventHandler: seedDataLambda
});
new cdk.CustomResource(this, "SeedDataResource", {
serviceToken: seedDataProvider.serviceToken,
properties: {
tableName,
user: process.env.ADMIN,
password: process.env.ADMINPASSWORD,
salt: process.env.SALT
}
});
}
}
This code works, but is it safe to pass through ADMIN, ADMINPASSWORD and SALT in this way? What are the security differences between this approach and accessing those values from AWS secrets manager? I also plan on using that SALT value when generating passwordDigest values for all new users, not just this admin user.
The properties values will be evaluated at deployment time. As such they will become part of CloudFormation template. The CloudFormation template can be viewed inside AWS Web Console. As such passing secrets around this way is questionable from security standpoint.
One way to overcome this is to store the secrets using AWS Secrets Manager. aws-cdk has good integration with it Secrets Manager. Once you create a secret you can import it via:
const mySecretFromName = secretsmanager.Secret.fromSecretNameV2(stack, 'SecretFromName', 'MySecret')
Unfortunately there's no support for resolving CloudFormation dynamic references in AWS Custom Resources. You can resolve the secret yourself though inside your lambda (seedDataLambda). The SqlRun repository provides an example.
Please remember to grant access to the secret for the custom resource lambda (seedLambda) e.g.
secret.grantRead(seedDataProvider.executionRole)
I want to deploy a Lex bot to my AWS account using CDK.
Looking at the API reference documentation I can't find a construct for Lex. Also, I found this issue on the CDK GitHub repository which confirms there is no CDK construct for Lex.
Is there any workaround to deploy the Lex bot or another tool for doing this ?
Edit: CloudFormation support for AWS Lex is now available, see Wesley Cheek's answer. Below is my original answer which solved the lack of CloudFormation support using custom resources.
There is! While perhaps a bit cumbersome, it's totally possible using custom resources.
Custom resources work by defining a lambda that handles creation and deletion events for the custom resource. Since it's possible to create and delete AWS Lex bots using the AWS API, we can make the lambda do this when the resource gets created or destroyed.
Here's a quick example I wrote in TS/JS:
CDK Code (TypeScript):
import * as path from 'path';
import * as cdk from '#aws-cdk/core';
import * as iam from '#aws-cdk/aws-iam';
import * as logs from '#aws-cdk/aws-logs';
import * as lambda from '#aws-cdk/aws-lambda';
import * as cr from '#aws-cdk/custom-resources';
export class CustomResourceExample extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Lambda that will handle the different cloudformation resource events
const lexBotResourceHandler = new lambda.Function(this, 'LexBotResourceHandler', {
code: lambda.Code.fromAsset(path.join(__dirname, 'lambdas')),
handler: 'lexBotResourceHandler.handler',
runtime: lambda.Runtime.NODEJS_14_X,
});
lexBotResourceHandler.addToRolePolicy(new iam.PolicyStatement({
resources: ['*'],
actions: ['lex:PutBot', 'lex:DeleteBot']
}))
// Custom resource provider, specifies how the custom resources should be created
const lexBotResourceProvider = new cr.Provider(this, 'LexBotResourceProvider', {
onEventHandler: lexBotResourceHandler,
logRetention: logs.RetentionDays.ONE_DAY // Default is to keep forever
});
// The custom resource, creating one of these will invoke the handler and create the bot
new cdk.CustomResource(this, 'ExampleLexBot', {
serviceToken: lexBotResourceProvider.serviceToken,
// These options will be passed down to the lambda
properties: {
locale: 'en-US',
childDirected: false
}
})
}
}
Lambda Code (JavaScript):
const AWS = require('aws-sdk');
const Lex = new AWS.LexModelBuildingService();
const onCreate = async (event) => {
await Lex.putBot({
name: event.LogicalResourceId,
locale: event.ResourceProperties.locale,
childDirected: Boolean(event.ResourceProperties.childDirected)
}).promise();
};
const onUpdate = async (event) => {
// TODO: Not implemented
};
const onDelete = async (event) => {
await Lex.deleteBot({
name: event.LogicalResourceId
}).promise();
};
exports.handler = async (event) => {
switch (event.RequestType) {
case 'Create':
await onCreate(event);
break;
case 'Update':
await onUpdate(event);
break;
case 'Delete':
await onDelete(event);
break;
}
};
I admit it's a very bare-bones example but hopefully it's enough to get you or anyone reading started and see how it could be built upon by adding more options and more custom resources (for example for intentions).
Deploying Lex using CloudFormation is now possible.
CDK support has also been added but it's only available as an L1 construct, meaning the CDK code is basically going to look like CloudFormation.
Also, since this feature just came out, some features may be missing or buggy. I have been unable to find a way to do channel integrations, and have had some problems with using image response cards, but otherwise have successfully deployed a bot and connected it with Lambda/S3 using CDK.
I have two Collections in Meteor, and trying to join them. As defined in collections/collections.js
Producers = new Mongo.Collection('producers');
Projects = new Mongo.Collection('projects');
Conceptually, one Producer has 0 to many Projects. One Project must have a Producer. So, a ProducerID field is in each Project document (row) in Mongo. I seeded the Mongo database with data.
When my template for viewing Projects is displayed, I want it to have access to the Producer's attributes.
In Iron Router's config (in /app.js), I have
Router.route('project', {
path: '/project/:name',
template: 'project',
waitOn: function() {
return Meteor.subscribe('ProjectInfo', this.params.name);
},
data: function() {
return Projects.find();
}
});
I have two publishes on the server (in /server/publish.js):
Meteor.publish("ProjectInfo", function(projectName) {
return Projects.find( {name: projectName} );
});
Meteor.publish("ProducerInfo", function(producerid) {
return Producers.find( {_id: producerid});
});
Question 1:
How to join? I definitely don't want to just throw Producer data into each Project, because it makes it harder to update the database when a Producer data changes.
Question 2:
Why does Iron Router need a "data:" field, when it already has a Subscribe in the waitOn?
data: function() {
return Projects.find();
}
Thanks in advance.
How to join: I'll just give you link to package doing just that: publish with relations
And you don't need data field when you publish, if your router loads OK and you have waitOn with publishes then on client side all your data should be visible when you do Producers.find() or Projects.find()
Although, waitOn requiers an array, so try
return [Meteor.subscribe('ProjectInfo', this.params.name)];
I have the Grails Spring Security plugin connecting to one Active Directory server with no problems. However, I need to connect to multiple servers. We have some users on one AD server and other users on a different server, so we need to try looking for users in both locations.
For example, in Java I have this working as below:
<authentication-manager>
<authentication-provider ref="provider1"/>
<authentication-provider ref="provider2"/>
...
</authentication-manager>
<ldap-server id="provider1"
url="ldap://LDAPSERVER1.mycompany.intranet"
manager-dn="OU=std_users,OU=users,DC=mycompany,DC=intranet"
manager-password="blah"/>
<ldap-server id="provider2"
url="ldap://DIFFERENT_LDAPSERVER.mycompany.intranet"
manager-dn="OU=std_users,OU=external_users,DC=mycompany,DC=intranet"
manager-password="blah"/>
In Grails I can configure one AD server but cannot work out how to configure more than one:
// LDAP config
grails.plugin.springsecurity.ldap.context.managerDn = 'CN=blah,OU=std_users,OU=users,DC=mycompany,DC=intranet'
grails.plugin.springsecurity.ldap.context.managerPassword = 'the_password'
grails.plugin.springsecurity.ldap.context.server = 'ldap://theserver.mycompany.intranet'
grails.plugin.springsecurity.ldap.authorities.ignorePartialResultException = true // typically needed for Active Directory
grails.plugin.springsecurity.ldap.search.base = 'OU=std_users,OU=users,DC=mycompany,DC=intranet'
grails.plugin.springsecurity.ldap.search.filter="sAMAccountName={0}" // for Active Directory you need this
grails.plugin.springsecurity.ldap.search.searchSubtree = true
grails.plugin.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
I know that you can create a space-separated list of servers but this won't work for me as it will only try one of the servers once it has a connection, whereas I need it to try looking for users in both.
I think I probably need to get stuck into the resources.groovy file but don't know where to start with this - has anyone configured multiple AD locations?
The only other idea I have is to create a virtual directory which brings together all the users in one directory. Can anyone suggest a good way of doing this? I have been looking at http://myvd.sourceforge.net/usecases.html
Any help would be appreciated. Have been googling all day and I am no closer to a solution.
Andrew's answer pointed me in the right direction and I now have this working.
It was A LOT easier to make this work using ActiveDirectoryLdapAuthenticationProvider. This is done as below:
In resources.groovy:
// Domain 1
ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
"mydomain.com",
"ldap://mydomain.com/"
)
// Domain 2
ldapAuthProvider2(ActiveDirectoryLdapAuthenticationProvider,
"mydomain2.com",
"ldap://mydomain2.com/"
)
In Config.groovy:
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1', 'ldapAuthProvider2']
This is all the code you need. You can pretty much remove all other grails.plugin.springsecurity.ldap.* settings in Config.groovy as they don't apply to this AD setup.
For documentation, see:
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory
If you aren't using AD and want the 'pure LDAP' version:
In resources.groovy:
// Create another ldap authentication provider
ldapAuthProvider2(org.springframework.security.ldap.authentication.LdapAuthenticationProvider,
ref("ldapAuthenticator2"),
ref("ldapAuthoritiesPopulator") // Use default
) {
// Can set other auth provider settings here
}
ldapAuthenticator2(org.springframework.security.ldap.authentication.BindAuthenticator, ref("contextSource2")) {
userSearch = ref("ldapUserSearch2")
}
// Set up the manager to read LDAP
contextSource2(DefaultSpringSecurityContextSource, grailsApplication.config.grails.plugin.springsecurity.ldap.context.server2) {
userDn = grailsApplication.config.grails.plugin.springsecurity.ldap.context.managerDn2 // Manager DN
password = grailsApplication.config.grails.plugin.springsecurity.ldap.context.managerPassword2
}
// Configuration for searching for user
ldapUserSearch2(FilterBasedLdapUserSearch, grailsApplication.config.grails.plugin.springsecurity.ldap.search.base2, grailsApplication.config.grails.plugin.springsecurity.ldap.search.filter2, ref('contextSource2')) {
}
And then in Config.groovy:
// Config for second LDAP AuthenticationProvider - used in resources.groovy
grails.plugin.springsecurity.ldap.context.managerDn2 = 'CN=MANAGER_USER,OU=Users,DC=mycompany,DC=com'
grails.plugin.springsecurity.ldap.context.managerPassword2 = 'manager_password'
grails.plugin.springsecurity.ldap.context.server2 = "ldap://the-ldap-server.com"
grails.plugin.springsecurity.ldap.search.base2 = 'OU=Users,DC=mycompany,DC=com'
grails.plugin.springsecurity.ldap.search.filter2 = "sAMAccountName={0}" // for Active Directory you need this
// Add the AuthenticationProvider to the list
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider', 'ldapAuthProvider2']
This link was very useful for finding out how to set this up:
https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/SpringSecurityLdapGrailsPlugin.groovy
The basic idea would be to construct a second, custom AuthenticationProvider bean within your application's grails-app/conf/spring/resources.groovy file, perhaps modeling it after ldapAuthProvider in https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/SpringSecurityLdapGrailsPlugin.groovy Then, you could add this custom LDAP authenticator bean to the grails.plugin.springsecurity.providerNames list in Config.groovy (or equivalent)
I'm using GitLab with an external issue tracker (JIRA), and it works well.
My problem is when I create a new GitLab project (using API), I have to go the GitLab's project settings and manually select the issue tracker I want to use and manually enter the project's id of my external issue tracker.
This screen will be more eloquent:
(source: bayimg.com)
(The two fields I am talking about are "Issue tracker" and "Project name or id in issues tracker")
So here is my question: is there any way to set up this two fields automatically, using API or other ? Currently, GitLab API does not mention anything about external issues tracker settings.
This code helped me to automatically set the GitLab's external issues-tracker settings, using Apache HttpClient and Jsoup.
This code is absolutely not 100% good, but it shows the main idea, wich is to recreate the corresponding POST request that the web form sends.
// 1 - Prepare the HttpClient object :
BasicCookieStore cookieStore = new BasicCookieStore();
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setRedirectStrategy(redirectStrategy)
.build();
try {
// 2 - Second you need to get the "CSRF Token", from a <meta> tag in the edit page :
HttpUriRequest getCsrfToken = RequestBuilder.get()
.setUri(new URI("http://localhost/_NAMESPACE_/_PROJECT_NAME_/edit"))
.build();
CloseableHttpResponse responseCsrf = httpclient.execute(getCsrfToken);
try {
HttpEntity entity = responseCsrf.getEntity();
Document doc = Jsoup.parse(EntityUtils.toString(entity));
String csrf_token = doc.getElementsByAttributeValue("name", "csrf-token").get(0).attr("content");
// 3 - Fill and submit the "edit" form with new values :
HttpUriRequest updateIssueTracker = RequestBuilder
.post()
.setUri(new URI("http://localhost/_NAMESPACE_/_PROJECT_NAME_"))
.addParameter("authenticity_token", csrf_token)
.addParameter("private_token", "_MY_PRIVATE_TOKEN_")
.addParameter("_method", "patch")
.addParameter("commit", "Save changes")
.addParameter("utf8", "✓")
.addParameter("project[issues_tracker]", "jira")
.addParameter("project[issues_tracker_id]", "_MY_JIRA_PROJECT_NAME_")
.addParameter("project[name]", "...")
...
.build();
CloseableHttpResponse responseSubmit = httpclient.execute(updateIssueTracker, httpContext);
} finally {
responseCsrf.close();
}
} finally {
httpclient.close();
}
Change _NAMESPACE_/_PROJECT_NAME_ to make it corresponds to your project URL, change _MY_PRIVATE_TOKEN_ with your admin account's token, and change _MY_JIRA_PROJECT_NAME_ with ... your jira project's name.