winglang / wing

A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
https://winglang.io
Other
4.77k stars 189 forks source link

Inputs (secrets and environment variables) #2726

Open skorfmann opened 1 year ago

skorfmann commented 1 year ago

Feature Spec

Either change the existing implementation of Secret to use a SSM Parameter or alternative introduce a new Construct along the lines of Config (e.g. Config.Parameter / Config.Secret) and make it clear by documentation that these serve different use-cases.

Use Cases

Right now https://docs.winglang.io/resources/secret are implemented as an AWS Secrets Manager resource in the targets tf-aws and awscdk. It might be better off as an SSM Parameter.

The use case I was expecting for the Wing Secret construct:

The use case I think the Wing Secret construct serves best right now:

The different use cases are reflected in the pricing of the resources. While the standard Parameter Store values are free (up to 10k per AWS account), the Secrets Manager value costs USD 0.40 / each per month.

Implementation Notes

Here's a related Slack conversation

Component

SDK

Community Notes

github-actions[bot] commented 11 months ago

Hi,

This issue hasn't seen activity in 60 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!

garysassano commented 9 months ago

Should users be able to create secrets inside a Wing app or we only allow importing them?

skorfmann commented 9 months ago

Should users be able to create secrets inside a Wing app or we only allow importing them?

Do you have an inflight use-case in mind?

edit: Or did you mean creating preflight with "in app"?

garysassano commented 8 months ago
bring cloud;

let secret = new cloud.Secret("projects/my-project/secrets/my-secret/versions/latest"); // this just assigns a string to the internal secretId attribute

let f = new cloud.Function(inflight () => {
  let secretValue = secret.getValue(cache: true); // this makes an API call to the cloud provider secret store (GCP in this case) to retrieve the secret value
  bucket.put("secret.txt", "Hello, ${secretValue}");
});

Advantages of the Proposed Approach:

No Need for Terraform Resources:

Seamless Integration:

Security:

Versatility:

skorfmann commented 8 months ago

Also, it should be noted that there's some tooling which enables managing SSM values, e.g.

At least a quick google search hasn't really surfaced anything like this for AWS Secrets Manager

Chriscbr commented 7 months ago

In the postgres library I had a use case to set a secret so a set of database credentials (which are generated at deployment time) can be used by inflight clients.

Here's the current hack I use to create an AWS SecretsManagerSecretVersion.

https://github.com/winglang/winglibs/blob/cce3adf4117b92a29bdd66a1f531d4a9eafb4866/postgres/lib.w#L67-L79

I think the way I've implemented it is OK since no credentials get stored in the terraform template, only in the terraform state. But my hack stops working if you compile to a platform besides tf-aws.

I feel like either some way to set a secret preflight:

new cloud.Secret(
  name: "my-secret",
  unsafeSecretString: ...
);

Or, a way to set the secret inflight:

inflight () => {
  secret.setValue(strValue);
};

... would solve the use case I mentioned. The only reservation I have about the first API (unsafeSecretString) is that it can be dangerous since it makes it easy for a user to accidentally put a sensitive string in a terraform file.

garysassano commented 7 months ago

In a previous message, I mentioned that creating a secret using Terraform is unnecessary. We only need the secret identifier (a string) for accessing it. I think it's a feasible solution to create a secret in the preflight using just the SDK.

import { SSMClient, PutParameterCommand, PutParameterResult } from "@aws-sdk/client-ssm";

async function createSSMSecret(secretName: string, secretValue: string): Promise<PutParameterResult> {
    const client = new SSMClient({ region: "your-region" });
    const createSecretParams = {
        Name: secretName,
        Value: secretValue,
        Type: "SecureString",
        Overwrite: false
    };

    try {
        const command = new PutParameterCommand(createSecretParams);
        const response = await client.send(command);
        console.log("SSM SecureString Created:", response);
        return response;
    } catch (error) {
        console.error("Error creating SSM SecureString:", error);
        throw error;
    }
}

// Usage
createSSMSecret("mySecretName", "mySecretValue").catch((error) => {
    console.error("Failed to create SSM SecureString:", error);
});

Afterwards, this string identifier can be passed to the inflight client for utilization. Regarding best practices for secret creation, using .env files is advisable, especially after the integration of #4608. If a secret already exists when attempting to create one, we could decide between generating a new version, overwriting the existing value or doing nothing.

Initially, my preference was to steer clear of generating secrets within Wing, leaning more towards handling them via the CLI of each cloud provider or through manual creation by users. Nevertheless, the method described here appears to be a secure enough approach for managing them.

github-actions[bot] commented 4 weeks ago

Hi,

This issue hasn't seen activity in 90 days. Therefore, we are marking this issue as stale for now. It will be closed after 7 days. Feel free to re-open this issue when there's an update or relevant information to be added. Thanks!