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

Multi-environment encryption key & team member separation #158

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

joelgallant
Copy link
Contributor

@joelgallant joelgallant commented Jun 20, 2021

Provides a backwards compatible model for teamMembers and encryptionKeys that respects and segregates the current environment that's active.

Before landing:

  • Using / passing the environmentOptions defined by meta file through into encryption
  • Using parsing context in the encryption parsing extension
  • Make encryption key IDs unambiguous across different environments - don't reuse the same IDs
  • E2E encryption workflow tests for multi-environment setup
  • Deciding the correct way to handle untrusting team members when using multi-env
  • Updates for documentation to reflect this capability
  • Unit testing the functions that accept environmentOptions
  • Unit testing for encryption parsing extension using environment-specific team members
  • Robust options in any related CLI commands
  • Provide support for suffixed environment variable keys (APP_CONFIG_SECRETS_KEY_PROD)
  • Beta testing w/ launchcode

Closes #105

@joelgallant joelgallant requested a review from gregnr June 20, 2021 00:10
@gregnr
Copy link
Contributor

gregnr commented Jun 20, 2021

Awesome work, this is becoming a much-needed feature at LC.

Trying to piece together how this is implemented. Hard to see the full picture w/o an example or tests, so forgive me if some of these questions are off-base:

  • Does this work by adding new properties under teamMembers and encryptionKeys which represent the environment, ie:

    .app-config.meta.yml:

    ...
    teamMembers:
      dev:
        - ...
        - ...
      qa:
        - ...
        - ...
      ...
    encryptionKeys:
      dev:
        - ...
        - ...
      qa:
        - ...
        - ...
      ...
  • If the above is true, have we considered using the $env extension somehow to be more idiomatic with other app-config patterns? (asking this in full naivety - maybe this can't work - or maybe is already the case!):

    .app-config.meta.yml:

    ...
    teamMembers:
      $env:
        dev:
          - ...
          - ...
        qa:
          - ...
          - ...
        ...
    encryptionKeys:
      $env:
        dev:
          - ...
          - ...
        qa:
          - ...
          - ...
        ...
  • Do we handle default or fallback environments for teamMembers/encryptionKeys? It would be handy to eg. have a default team member/encryption key for all environments except production, which has a separate set of team members/encryption keys.

    If we're using $env, I imagine this would "just work".

  • For CLI, am I correct to say we have a new --environment-override flag when handling secrets? Would specifying the env via environment variable also work? eg:

    NODE_ENV=qa app-config secret encrypt
  • For secret CLI, do we consider environmentAliases and environmentSourceNames overrides set in the meta file? (there might be a potential chicken-egg scenario here).

    Ref: https://app-config.dev/guide/intro/settings.html#parse-and-loading-configuration

  • If a team member is trusted in both qa and prod (for example), does their public key exist twice in the meta file, once per environment?

    For the record, this is something I'm okay with and prefer - it keeps the implementation simple (vs. trying to normalize team member keys in one place, then reference them in multiple environments).

  • Ideally we should provide a friendly "environment aware" error message in situations where app-config is trying to decrypt a config value that came from an environment that the current user isn't trusted in.

  • I can predict us often needing to trust the "CI" team member in multiple environments. I imagine security best practice here would be to create separate CI keys per environment (in case one key is compromised, the scope is limited). This is going to make things tricky for CI's injected APP_CONFIG_SECRETS_KEY environment variable - how do we have multiple?

    Edit: In GitLab Premium, environment-specific variables can be set (ie. different APP_CONFIG_SECRETS_KEY for qa vs. prod). Not sure how easy this is to do in other CI systems.

  • With regard to untrusting - this should be as simple as setting the correct environment in the CLI command as part of the untrust operation, correct? Or is there more to this that I'm missing?

@joelgallant
Copy link
Contributor Author

If the above is true, have we considered using the $env extension somehow to be more idiomatic with other app-config patterns? (asking this in full naivety - maybe this can't work - or maybe is already the case!):

This could certainly be done (you're correct that I omitted $env because it has to be treated first-class in encryption stuff anyways). We have warnings when keys are $ prefixed and not processed by a parsing extension, although I don't think that applies here. I'm not strongly opinionated other than saying "$env isn't technically being used here", but that might be an implementation detail that's hidden (ie what if $env was extended to support new functionality but couldn't be reflected here).

Do we handle default or fallback environments for teamMembers/encryptionKeys? It would be handy to eg. have a default team member/encryption key for all environments except production, which has a separate set of team members/encryption keys.

Yup, this is built in to the PoC, ditto for none.

For CLI, am I correct to say we have a new --environment-override flag when handling secrets? Would specifying the env via environment variable also work?

Both ways are intended to work, for sure. The flags are there as a consistency measure, and largely for --environment-variable-name.

For secret CLI, do we consider environmentAliases and environmentSourceNames overrides set in the meta file? (there might be a potential chicken-egg scenario here).

This is (one of) the reason that I didn't actually use the $env parser. The environment resolving happens after meta file has been loaded. I think I missed actually passing those values through, but that's the spirit of why it's not quite as simple as it looks.

If a team member is trusted in both qa and prod (for example), does their public key exist twice in the meta file, once per environment?

Yup, this is the simplest way to handle it. Indeed, I've thought about similar extensions to $env for deduplication like this (#111). You could use YAML anchors, but they'd be erased if you use the CLI to write the meta file again.

Ideally we should provide a friendly "environment aware" error message in situations where app-config is trying to decrypt a config value that came from an environment that the current user isn't trusted in.

I put some messages in already to try and be specific, but they could be improved.

I can predict us often needing to trust the "CI" team member in multiple environments. I imagine security best practice here would be to create separate CI keys per environment (in case one key is compromised, the scope is limited). This is going to make things tricky for CI's injected APP_CONFIG_SECRETS_KEY environment variable - how do we have multiple?

Yeah, my answer here would default to "whatever CI providers give you" to define that variable differently per env. It's not too difficult to support a suffixed key though (APP_CONFIG_SECRETS_KEY_PROD). Think it's worth it?

With regard to untrusting - this should be as simple as setting the correct environment in the CLI command as part of the untrust operation, correct? Or is there more to this that I'm missing?

Yeah naturally, it will only remove that team member for the current environment (set by override or APP_CONFIG_ENV). Normally though, I would assume the use case for untrusting involves untrusting for all environments. Maybe it's as simple as an extra CLI flag for that, or a prompt.

@gregnr
Copy link
Contributor

gregnr commented Jun 23, 2021

I'm not strongly opinionated other than saying "$env isn't technically being used here", but that might be an implementation detail that's hidden (ie what if $env was extended to support new functionality but couldn't be reflected here).

I agree 100%. If there's no way to fully support $env in the meta file, we shouldn't be calling it $env. The inconsistency will cause too much confusion.

Yup, this is built in to the PoC, ditto for none.

Thoughts on calling this default instead of none? Eg. in situations where we do set an environment, but that environment doesn't exist under teamMembers.

The environment resolving happens after meta file has been loaded.

Yeah, this makes sense.

Normally though, I would assume the use case for untrusting involves untrusting for all environments. Maybe it's as simple as an extra CLI flag for that, or a prompt.

Good point. I think those are both great options.

@joelgallant
Copy link
Contributor Author

Thoughts on calling this default instead of none? Eg. in situations where we do set an environment, but that environment doesn't exist under teamMembers.

This is already the case, both are distinct and supported in $env and here now.

@gregnr
Copy link
Contributor

gregnr commented Jun 24, 2021

Thoughts on calling this default instead of none? Eg. in situations where we do set an environment, but that environment doesn't exist under teamMembers.

This is already the case, both are distinct and supported in $env and here now.

Amazing.

This PR LGTM pending tests, documentation, etc.

@codecov-commenter
Copy link

codecov-commenter commented Jun 27, 2021

Codecov Report

Merging #158 (6b7575a) into master (8e854db) will decrease coverage by 1.27%.
The diff coverage is 41.60%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #158      +/-   ##
==========================================
- Coverage   80.43%   79.16%   -1.28%     
==========================================
  Files          42       42              
  Lines        2453     2529      +76     
  Branches      586      608      +22     
==========================================
+ Hits         1973     2002      +29     
- Misses        480      527      +47     
Impacted Files Coverage Δ
app-config-meta/src/index.ts 66.66% <ø> (ø)
app-config-cli/src/index.ts 50.78% <7.69%> (-2.31%) ⬇️
app-config-encryption/src/secret-agent.ts 71.31% <33.33%> (-0.59%) ⬇️
app-config-encryption/src/encryption.ts 70.43% <49.43%> (-5.71%) ⬇️
app-config-encryption/src/index.ts 94.44% <100.00%> (+0.69%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8e854db...6b7575a. Read the comment docs.

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

Successfully merging this pull request may close these issues.

Provide support for environment-specific encryption teamMembers
3 participants