-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
14,131 additions
and
4,416 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
# Typescript | ||
*.js | ||
*.d.ts | ||
_old/ | ||
|
||
# No sops binary | ||
provider/sops | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import * as cfn from '@aws-cdk/aws-cloudformation'; | ||
import * as iam from '@aws-cdk/aws-iam'; | ||
import * as kms from '@aws-cdk/aws-kms'; | ||
import * as lambda from '@aws-cdk/aws-lambda'; | ||
import * as s3Assets from '@aws-cdk/aws-s3-assets'; | ||
import * as secretsManager from '@aws-cdk/aws-secretsmanager'; | ||
import * as cdk from '@aws-cdk/core'; | ||
import * as customResource from '@aws-cdk/custom-resources'; | ||
import * as common from './common'; | ||
export * from './common'; | ||
|
||
export interface SopsSecretsManagerProps extends common.SopsSecretsManagerBaseProps { | ||
readonly secret?: secretsManager.Secret | secretsManager.ISecret; | ||
readonly asset?: s3Assets.Asset; | ||
readonly kmsKey?: kms.IKey; | ||
} | ||
|
||
class SopsSecretsManagerProvider extends cdk.Construct { | ||
public readonly provider: customResource.Provider; | ||
|
||
public static getOrCreate(scope: cdk.Construct, forceNode12: boolean): customResource.Provider { | ||
const stack = cdk.Stack.of(scope); | ||
const id = common.providerId; | ||
const x = (stack.node.tryFindChild(id) as SopsSecretsManagerProvider) || new SopsSecretsManagerProvider(stack, id, forceNode12); | ||
return x.provider; | ||
} | ||
|
||
constructor(scope: cdk.Construct, id: string, forceNode12: boolean) { | ||
super(scope, id); | ||
|
||
const policyStatements: Array<iam.PolicyStatement> = []; | ||
for (const statement of common.providerPolicyStatements) { | ||
policyStatements.push(new iam.PolicyStatement(statement)); | ||
} | ||
|
||
this.provider = new customResource.Provider(this, common.providerLogicalId, { | ||
onEventHandler: new lambda.Function(this, common.providerFunctionLogicalId, { | ||
code: lambda.Code.fromAsset(common.providerCodePath), | ||
runtime: lambda.Runtime.NODEJS_12_X, | ||
handler: common.providerHandler, | ||
timeout: cdk.Duration.minutes(common.providerTimoutMinutes), | ||
initialPolicy: policyStatements, | ||
}), | ||
}); | ||
|
||
if (forceNode12) { | ||
// Find the provider lambda and hack away | ||
// This section hacks the CDK's utility lambda to use Node 12, | ||
// which uses Node 10 in cdk <1.94.0. This is no longer | ||
// deployable as of July 30, 2021. | ||
const lambdaFn = (this.provider.node.findChild('framework-onEvent') as unknown) as lambda.Function; | ||
const cfnLambdaFn = lambdaFn.node.defaultChild as lambda.CfnFunction; | ||
cfnLambdaFn.addPropertyOverride('Runtime', lambda.Runtime.NODEJS_12_X.toString()); | ||
} | ||
} | ||
} | ||
|
||
export class SopsSecretsManager extends cdk.Construct { | ||
public readonly secret: secretsManager.Secret | undefined; | ||
public readonly secretArn: string; | ||
public readonly asset: s3Assets.Asset; | ||
|
||
constructor(scope: cdk.Construct, id: string, props: SopsSecretsManagerProps) { | ||
super(scope, id); | ||
|
||
if (props.secret && props.secretName) { | ||
throw new Error('Cannot set both secret and secretName'); | ||
} else if (props.secret) { | ||
this.secretArn = props.secret.secretArn; | ||
this.secret = undefined; | ||
} else if (props.secretName) { | ||
this.secret = new secretsManager.Secret(this, 'Secret', { | ||
secretName: props.secretName, | ||
}); | ||
this.secretArn = this.secret.secretArn; | ||
} else { | ||
throw new Error('Must set one of secret or secretName'); | ||
} | ||
this.asset = this.getAsset(props.asset, props.path); | ||
|
||
if (props.wholeFile && props.mappings) { | ||
throw new Error('Cannot set mappings and set wholeFile to true'); | ||
} else if (!props.wholeFile && !props.mappings) { | ||
throw new Error('Must set mappings or set wholeFile to true'); | ||
} | ||
|
||
new cfn.CustomResource(this, 'Resource', { | ||
provider: SopsSecretsManagerProvider.getOrCreate(this, props.hackToForceNode12 ?? false), | ||
resourceType: 'Custom::SopsSecretsManager', | ||
properties: { | ||
SecretArn: this.secretArn, | ||
S3Bucket: this.asset.s3BucketName, | ||
S3Path: this.asset.s3ObjectKey, | ||
SourceHash: this.asset.sourceHash, | ||
KMSKeyArn: props.kmsKey?.keyArn, | ||
Mappings: JSON.stringify(props.mappings || {}), | ||
WholeFile: props.wholeFile || false, | ||
FileType: props.fileType, | ||
}, | ||
}); | ||
} | ||
|
||
public getAsset(asset?: s3Assets.Asset, secretFilePath?: string): s3Assets.Asset { | ||
if (asset && secretFilePath) { | ||
throw new Error('Cannot set both asset and path'); | ||
} | ||
|
||
if (asset) { | ||
return asset; | ||
} | ||
|
||
if (secretFilePath) { | ||
return new s3Assets.Asset(this, 'SopsAsset', { | ||
path: secretFilePath, | ||
}); | ||
} | ||
|
||
throw new Error('Must set one of asset or path'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import * as iam from 'aws-cdk-lib/aws-iam'; | ||
import * as kms from 'aws-cdk-lib/aws-kms'; | ||
import * as lambda from 'aws-cdk-lib/aws-lambda'; | ||
import * as s3Assets from 'aws-cdk-lib/aws-s3-assets'; | ||
import * as secretsManager from 'aws-cdk-lib/aws-secretsmanager'; | ||
import * as cdk from 'aws-cdk-lib'; | ||
import * as constructs from 'constructs'; | ||
import * as customResource from 'aws-cdk-lib/custom-resources'; | ||
import * as common from './common'; | ||
export * from './common'; | ||
|
||
export interface SopsSecretsManagerProps extends common.SopsSecretsManagerBaseProps { | ||
readonly secret?: secretsManager.Secret | secretsManager.ISecret; | ||
readonly asset?: s3Assets.Asset; | ||
readonly kmsKey?: kms.IKey; | ||
} | ||
|
||
class SopsSecretsManagerProvider extends constructs.Construct { | ||
public readonly provider: customResource.Provider; | ||
|
||
public static getOrCreate(scope: constructs.Construct): customResource.Provider { | ||
const stack = cdk.Stack.of(scope); | ||
const id = common.providerId; | ||
const x = (stack.node.tryFindChild(id) as SopsSecretsManagerProvider) || new SopsSecretsManagerProvider(stack, id); | ||
return x.provider; | ||
} | ||
|
||
constructor(scope: constructs.Construct, id: string) { | ||
super(scope, id); | ||
|
||
const policyStatements: Array<iam.PolicyStatement> = []; | ||
for (const statement of common.providerPolicyStatements) { | ||
policyStatements.push(new iam.PolicyStatement(statement)); | ||
} | ||
|
||
this.provider = new customResource.Provider(this, common.providerLogicalId, { | ||
onEventHandler: new lambda.Function(this, common.providerFunctionLogicalId, { | ||
code: lambda.Code.fromAsset(common.providerCodePath), | ||
runtime: lambda.Runtime.NODEJS_12_X, | ||
handler: common.providerHandler, | ||
timeout: cdk.Duration.minutes(common.providerTimoutMinutes), | ||
initialPolicy: policyStatements, | ||
}), | ||
}); | ||
} | ||
} | ||
|
||
export class SopsSecretsManager extends constructs.Construct { | ||
public readonly secret: secretsManager.Secret | undefined; | ||
public readonly secretArn: string; | ||
public readonly asset: s3Assets.Asset; | ||
|
||
constructor(scope: constructs.Construct, id: string, props: SopsSecretsManagerProps) { | ||
super(scope, id); | ||
|
||
if (props.secret && props.secretName) { | ||
throw new Error('Cannot set both secret and secretName'); | ||
} else if (props.secret) { | ||
this.secretArn = props.secret.secretArn; | ||
this.secret = undefined; | ||
} else if (props.secretName) { | ||
this.secret = new secretsManager.Secret(this, 'Secret', { | ||
secretName: props.secretName, | ||
}); | ||
this.secretArn = this.secret.secretArn; | ||
} else { | ||
throw new Error('Must set one of secret or secretName'); | ||
} | ||
this.asset = this.getAsset(props.asset, props.path); | ||
|
||
if (props.wholeFile && props.mappings) { | ||
throw new Error('Cannot set mappings and set wholeFile to true'); | ||
} else if (!props.wholeFile && !props.mappings) { | ||
throw new Error('Must set mappings or set wholeFile to true'); | ||
} | ||
|
||
const provider = SopsSecretsManagerProvider.getOrCreate(this); | ||
|
||
new cdk.CustomResource(this, 'Resource', { | ||
serviceToken: provider.serviceToken, | ||
resourceType: 'Custom::SopsSecretsManager', | ||
properties: { | ||
SecretArn: this.secretArn, | ||
S3Bucket: this.asset.s3BucketName, | ||
S3Path: this.asset.s3ObjectKey, | ||
SourceHash: this.asset.assetHash, | ||
KMSKeyArn: props.kmsKey?.keyArn, | ||
Mappings: JSON.stringify(props.mappings || {}), | ||
WholeFile: props.wholeFile || false, | ||
FileType: props.fileType, | ||
}, | ||
}); | ||
} | ||
|
||
public getAsset(asset?: s3Assets.Asset, secretFilePath?: string): s3Assets.Asset { | ||
if (asset && secretFilePath) { | ||
throw new Error('Cannot set both asset and path'); | ||
} | ||
|
||
if (asset) { | ||
return asset; | ||
} | ||
|
||
if (secretFilePath) { | ||
return new s3Assets.Asset(this, 'SopsAsset', { | ||
path: secretFilePath, | ||
}); | ||
} | ||
|
||
throw new Error('Must set one of asset or path'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import * as path from 'path'; | ||
|
||
export type SopsSecretsManagerEncoding = 'string' | 'json'; | ||
|
||
export type SopsSecretsManagerFileType = 'yaml' | 'json'; | ||
|
||
export interface SopsSecretsManagerMapping { | ||
path: Array<string>; | ||
encoding?: SopsSecretsManagerEncoding; | ||
} | ||
|
||
export interface SopsSecretsManagerMappings { | ||
[key: string]: SopsSecretsManagerMapping; | ||
} | ||
|
||
export interface SopsSecretsManagerBaseProps { | ||
readonly secret?: unknown; | ||
readonly secretName?: string; | ||
readonly asset?: unknown; | ||
readonly path?: string; | ||
readonly kmsKey?: unknown; | ||
readonly mappings?: SopsSecretsManagerMappings; | ||
readonly wholeFile?: boolean; | ||
readonly fileType?: SopsSecretsManagerFileType; | ||
readonly hackToForceNode12?: boolean; | ||
} | ||
|
||
export const providerId = 'com.isotoma.cdk.custom-resources.sops-secrets-manager'; | ||
export const providerLogicalId = 'sops-secrets-manager-provider'; | ||
export const providerFunctionLogicalId = 'sops-secrets-manager-event'; | ||
export const providerCodePath = path.join(__dirname, 'provider'); | ||
export const providerHandler = 'index.onEvent'; | ||
export const providerTimoutMinutes = 5; | ||
|
||
interface PolicyStatement { | ||
resources: Array<string>; | ||
actions: Array<string>; | ||
}; | ||
|
||
export const providerPolicyStatements: Array<PolicyStatement> = [{ | ||
resources: ['*'], | ||
actions: ['s3:GetObject*', 's3:GetBucket*', 's3:List*', 's3:DeleteObject*', 's3:PutObject*', 's3:Abort*'], | ||
}, { | ||
resources: ['*'], | ||
actions: ['kms:*'], | ||
}, { | ||
resources: ['*'], | ||
actions: ['secretsmanager:*'], | ||
}]; |
Oops, something went wrong.