firebase / firebase-functions

Firebase SDK for Cloud Functions
https://firebase.google.com/docs/functions/
MIT License
1.03k stars 204 forks source link

defineSecret creates a list of values in Secret Manager #1567

Closed jtmuller5 closed 4 months ago

jtmuller5 commented 6 months ago

Related issues

[REQUIRED] Version info

node: v20.13.1

firebase-functions: ^5.0.0

firebase-tools:

firebase-admin: 13.9.0

[REQUIRED] Test case

Original Stack Overflow question: https://stackoverflow.com/a/78496117/12806961

[REQUIRED] Steps to reproduce

  1. Create a new Firebase Genkit project (firebase init genkit)

  2. export GOOGLE_GENAI_API_KEY={key}

  3. Add these lines at the top of the file:

    import { defineSecret } from "firebase-functions/params";
    defineSecret("GOOGLE_GENAI_API_KEY");
  4. Run firebase deploy --only functions

  5. Navigate to GCP > Cloud Functions > Functions > Select Function > Variables Tab

The GOOGLE_GENAI_API_KEY variable is a list, not a string.

[REQUIRED] Expected behavior

DefineSecret should create a single string environment variable and not a list with a single string value.

[REQUIRED] Actual behavior

image

Were you able to successfully deploy your functions?

Yes but it didn't work. I needed to change defineSecret to defineString.

google-oss-bot commented 6 months ago

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

jtmuller5 commented 6 months ago

I was following the steps in this article: https://firebase.google.com/docs/genkit/firebase

I also tested with another env variable. I created a .env file in my functions folder like this:

GOOGLE_GENAI_API_KEY=boo
TEST=AIzaS.....

And I only called defineSecret once:

import { defineSecret } from "firebase-functions/params";
defineSecret("GOOGLE_GENAI_API_KEY");

And after running firebase deploy --only functions I see this in GCP:

image

offline-first commented 6 months ago

Same issue. I fixed it with this solution:

export class FixedSecretParam {
    constructor(private param: SecretParam){}
    value(): string {
        return (JSON.parse(this.param.value()) as string[])[0]
    }
}

export const IPA_SHARED_SECRET = new FixedSecretParam(defineSecret('MY_SECRET'))
exaby73 commented 6 months ago

I'm a bit confused here. Secrets should not be visible in the Runtime environment variables section, but instead in the Secrets section

google-oss-bot commented 5 months ago

Hey @jtmuller5. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 3 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot commented 5 months ago

Since there haven't been any recent updates here, I am going to close this issue.

@jtmuller5 if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

taeold commented 5 months ago

Hi folks!

After reviewing the issue, it looks like there's a common footgun when using defineSecret to define your secrets.

Unlike other parameters, like the one you create with defineString, values of secret parameters are never meant to be provided by your .env files. Storing secrets directly in your code or configuration files poses a significant security risk, especially if your code is under version control.

For example, if you have:

import { defineSecret } from "firebase-functions/params";
const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");

Then instead of putting your API key in your .env file:

GOOGLE_GENAI_API_KEY=abcedfg... # DON'T DO THIS

Simply deploy your function. During the deployment process, the Firebase CLI will prompt you for the value of the secret and securely store the API key in Google Cloud Secret Manager. This approach ensures your secrets are managed securely outside of your codebase.

For example:

$ firebase deploy --only functions

=== Deploying to 'my-project'...

i  deploying functions
? This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as GOOGLE_GENAI_API_KEY. Enter a value for GOOGLE_GENAI_API_KEY:
[input is hidden]
...

The purpose of secret params is to avoid storing sensitive information in .env files, reducing the risk of accidentally exposing secrets in your source code.

When using a secret in your function, remember to bind it:

const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");

onInit(() => {
configureGenkit({
  plugins: [
    firebase(),
    googleAI( { apiKey: apiKey.value() }), # you can access the value like this
  ],
  logLevel: "debug",
  enableTracingAndMetrics: true,
});

export const menuSuggestionFlow = onCall(
  // LOOK HERE: YOU NEED TO BIND THE SECRET TO THE FUNCTION
  { secrets: [apiKey] },
  async (req) => {
    ...
  }
);

Now, there appears to be a bug where if you define a secret (e.g., defineSecret("ABC")) and set its value in your .env file (e.g., ABC=123), the CLI sets the environment variable ABC to "[123]". This is likely a bug, but one you'll only encounter when not using secret parameters as intended.

We'll investigate fixing this in the Firebase CLI codebase. In the meantime, please double-check that you're correctly setting and binding your secrets in your functions.

Let me know if you have any questions!