Skip to content

Commit

Permalink
add config secret resources to iac and modularize
Browse files Browse the repository at this point in the history
  • Loading branch information
jhsinger-klotho committed Feb 21, 2023
1 parent ff8ec4f commit e828852
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 53 deletions.
50 changes: 2 additions & 48 deletions pkg/infra/pulumi_aws/deploylib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,8 @@ export class CloudCCLib {
return [v.Name, bucket.bucket]
case Resource.config:
if (v.Value == 'secret_name') {
return [v.Name, `${this.name}_${v.ResourceID}`] // TODO: Make this use the secret once we figure out how to bootstrap
const secret: aws.secretsmanager.Secret = this.secrets.get(v.ResourceID)!
return [v.Name, secret.name]
}
default:
throw new Error('unsupported kind')
Expand Down Expand Up @@ -1013,53 +1014,6 @@ export class CloudCCLib {
)
}

public setupSecrets(secrets: string[]) {
for (const secret of secrets) {
const secretName = `${this.name}-${secret}`
validate(secretName, AwsSanitizer.SecretsManager.secret.nameValidation())
let awsSecret: aws.secretsmanager.Secret
if (this.secrets.has(secret)) {
awsSecret = this.secrets.get(secret)!
} else {
awsSecret = new aws.secretsmanager.Secret(
`${secret}`,
{
name: secretName,
recoveryWindowInDays: 0,
},
{ protect: this.protect }
)
if (fs.existsSync(secret)) {
new aws.secretsmanager.SecretVersion(
`${secret}`,
{
secretId: awsSecret.id,
secretBinary: fs.readFileSync(secret).toString('base64'),
},
{ protect: this.protect }
)
}
this.secrets.set(secret, awsSecret)
}
this.topology.topologyIconData.forEach((resource) => {
if (resource.kind == Resource.secret) {
this.topology.topologyEdgeData.forEach((edge) => {
if (edge.target == resource.id) {
this.addPolicyStatementForName(
this.resourceIdToResource.get(edge.source).title,
{
Effect: 'Allow',
Action: ['secretsmanager:GetSecretValue'],
Resource: [awsSecret.arn],
}
)
}
})
}
})
}
}

public setupRDS(orm: string, args: Partial<aws.rds.InstanceArgs>) {
if (!this.subnetGroup) {
const subnetGroupName = sanitized(AwsSanitizer.RDS.dbSubnetGroup.nameValidation())`${h(
Expand Down
90 changes: 90 additions & 0 deletions pkg/infra/pulumi_aws/iac/secrets_manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { CloudCCLib, Resource } from '../deploylib'
import * as aws from '@pulumi/aws'
import * as pulumi from '@pulumi/pulumi'

import * as fs from 'fs'

import { hash as h, validate } from './sanitization/sanitizer'
import AwsSanitizer from './sanitization/aws'

export interface Secret {
Name: string
FilePath: string
Params: any
}

export class SecretsManager {
constructor(private lib: CloudCCLib, secrets: Secret[]) {
for (const secret of secrets) {
if (secret.FilePath == '') {
secret.FilePath = this.getSecretFilePath(secret.Name)
}
if (secret.FilePath != '') {
this.setupSecretFromFile(secret)
} else {
throw new Error('Unsupported value type for secret')
}
}
}

private getSecretFilePath(secretName: string): string {
const config = new pulumi.Config()
const valuesFile = config.require(`config-${secretName}-FilePath`)
return valuesFile
}

private setupSecretFromFile(secret: Secret) {
const secretName = `${this.lib.name}-${secret.Name}`
validate(secretName, AwsSanitizer.SecretsManager.secret.nameValidation())
let awsSecret: aws.secretsmanager.Secret
if (this.lib.secrets.has(secret.Name)) {
awsSecret = this.lib.secrets.get(secret.Name)!
} else {
awsSecret = new aws.secretsmanager.Secret(
`${secret.Name}`,
{
name: secretName,
recoveryWindowInDays: 0,
},
{ protect: this.lib.protect }
)
if (fs.existsSync(secret.FilePath)) {
new aws.secretsmanager.SecretVersion(
`${secret.Name}`,
{
secretId: awsSecret.id,
secretBinary: fs.readFileSync(secret.FilePath).toString('base64'),
},
{ protect: this.lib.protect }
)
}
this.lib.secrets.set(secret.Name, awsSecret)
}
this.addPermissions(awsSecret.arn)
}

private addPermissions(secretArn: pulumi.Output<string>) {
this.lib.topology.topologyIconData.forEach((resource) => {
if (
resource.kind == Resource.secret ||
(resource.kind == Resource.config && resource.type == 'secrets_manager')
) {
this.lib.topology.topologyEdgeData.forEach((edge) => {
if (edge.target == resource.id) {
this.lib.addPolicyStatementForName(
this.lib.resourceIdToResource.get(edge.source).title,
{
Effect: 'Allow',
Action: [
'secretsmanager:GetSecretValue',
'secretsmanager:DescribeSecret',
],
Resource: [secretArn],
}
)
}
})
}
})
}
}
8 changes: 6 additions & 2 deletions pkg/infra/pulumi_aws/index.ts.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import {createStaticS3Website} from './iac/static_s3_website'
import { Cloudfront } from './iac/cloudfront'
{{end}}

{{- if .SecretManagerSecrets}}
import { SecretsManager } from './iac/secrets_manager'
{{end}}

export = async () => {
const minimumNodeVersion = 16
const nodeVersionMatch = process.version.match(/^v(\d+)/)
Expand Down Expand Up @@ -84,8 +88,8 @@ export = async () => {
cloudLib.setupKV();
{{- end}}

{{- if .Secrets}}
cloudLib.setupSecrets({{json .Secrets}});
{{- if .SecretManagerSecrets}}
new SecretsManager(cloudLib, {{json .SecretManagerSecrets}});
{{- end}}

{{range $orm := .ORMs -}}
Expand Down
3 changes: 3 additions & 0 deletions pkg/infra/pulumi_aws/plugin_iac.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ func (p Plugin) Transform(result *core.CompilationResult, deps *core.Dependencie
if len(data.APIGateways) > 0 {
addFile("iac/api_gateway.ts")
}
if len(data.SecretManagerSecrets) > 0 {
addFile("iac/secrets_manager.ts")
}

addFile("deploylib.ts")
addFile("package.json")
Expand Down
7 changes: 6 additions & 1 deletion pkg/provider/aws/infra_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ func (a *AWS) Transform(result *core.CompilationResult, deps *core.Dependencies)

case *core.Secrets:
if res.Kind == core.PersistSecretKind {
data.Secrets = append(data.Secrets, res.Secrets...)
for _, secret := range res.Secrets {
data.SecretManagerSecrets = append(data.SecretManagerSecrets, provider.Config{
Name: secret,
FilePath: secret,
})
}
}
case *core.Topology:
data.Topology = res.GetTopologyData()
Expand Down
5 changes: 3 additions & 2 deletions pkg/provider/infra_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ type (
}

Config struct {
Name string
Params config.InfraParams
Name string
FilePath string
Params config.InfraParams
}

Gateway struct {
Expand Down

0 comments on commit e828852

Please sign in to comment.