Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secrets not bound to a function have a value when using the emulator #6905

Open
tzappia opened this issue Mar 23, 2024 · 3 comments
Open

Secrets not bound to a function have a value when using the emulator #6905

tzappia opened this issue Mar 23, 2024 · 3 comments

Comments

@tzappia
Copy link

tzappia commented Mar 23, 2024

[REQUIRED] Environment info

firebase-tools: 13.5.2

Platform: macOS

[REQUIRED] Test case

import * as logger from "firebase-functions/logger";
import { defineSecret } from 'firebase-functions/params';
import { onRequest } from "firebase-functions/v2/https";

const secret = defineSecret('SECRET');

export const test = onRequest(
  // Do not bind the secrets!
  // {
  //   secrets: [secret],
  // },
  (req, res) => {
    logger.info(`The secret is ${secret.value()}`);
  });

[REQUIRED] Steps to reproduce

Run the above function in both production and in an environment using the emulator. I used .secret.local and .env.local to give the secret a value for the emulated environment and used Cloud Secret Manager to provide a value for the production environment.

[REQUIRED] Expected behavior

Without secrets being bound to the function, secret.value() should be undefined at runtime. See documentation.

[REQUIRED] Actual behavior

In production, secret.value() is undefined. In the emulated environment, the value from .secret.local is returned.

Additional observations

There were some additional issues observed during testing of this. It may be expected behaviour, but if not I'm happy to raise additional bugs.

  1. When the value of the secret is updated in production (by adding a new version in Cloud Secret Manager) the new value is not returned by secret.value() immediately. I had to redeploy the function to start seeing the new value.
  2. If I properly bind the secrets to the function, run the function, then change the code to no longer bind the secrets (as in the test case) then secret.value() continues to return the value instead of undefined.
@aalej
Copy link
Contributor

aalej commented Mar 25, 2024

Hey @tzappia, thanks for the detailed report and for sharing your observations. This is a lot of info! I was able to reproduce the behavior you mentioned. When using the emulator, the value of the secret in .secret.local is being used. However, when using the deployed function, SECRET is not being loaded. I’ll raise this issue to our engineering team.

To address one of the issues you observed(When the value of the secret is updated in production...). When you set a new value for a secret, you would also have to update the function which references that secret so that it would pick up the latest value. See this documentation for reference. As for the second one, I’ll also inform our engineering team about the issue.

@inlined
Copy link
Member

inlined commented Mar 26, 2024

Thank you for the bug report. A few comments:

When the value of the secret is updated in production (by adding a new version in Cloud Secret Manager) the new value is not returned by secret.value() immediately. I had to redeploy the function to start seeing the new value.

This is the expected behavior. Secrets in environment variables are fixed at container start time. While there is API support for pinning to the version "latest", Firebase opted to not allow this because it will lead to a fleet where different containers of the same function are using different versions of the secret. If you call firebase functions:secrets:set it will identify all functions bound to a secret and offer to redeploy them to update your secret value.

In production, secret.value() is undefined. In the emulated environment, the value from .secret.local is returned.

This is a tough problem that may not be fixable without other dramatic impact. Unlike production, the emulator runs all functions as a single process. This is to help make sure --inspect-functions is usable. To emulate secrets binding in the emulator, we would have to move the process model closer to production. This may be a good thing to do, but we will not have time to do this work until after I/O. We'll call this out in our documentation until we have a chance to revisit however.

@inlined inlined removed their assignment Mar 26, 2024
@kdawgwilk
Copy link

In production, secret.value() is undefined. In the emulated environment, the value from .secret.local is returned.

This issue has bitten us sooooo many times. Basically every engineer we onboard makes the mistake of adding a secret but forgets to add the secret to the functions that need it but when they test locally everything works as expected. It then gets deployed to production and BAM! It blows up. Has anyone figured out a way to enforce this for local dev with the emulators with just a wrapper function?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants