docs(core): cache security guide (#20469)

This commit is contained in:
Isaac Mann 2023-11-30 15:07:27 -05:00 committed by GitHub
parent 2cce1f5711
commit aba1802570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 174 additions and 130 deletions

View File

@ -250,23 +250,13 @@
"tags": ["distribute-task-execution"]
},
{
"id": "scenarios",
"name": "Security Scenarios",
"id": "cache-security",
"name": "Cache Security",
"description": "",
"file": "nx-cloud/concepts/scenarios",
"file": "nx-cloud/concepts/cache-security",
"itemList": [],
"isExternal": false,
"path": "/ci/concepts/scenarios",
"tags": []
},
{
"id": "encryption",
"name": "End to End Encryption",
"description": "",
"file": "nx-cloud/concepts/encryption",
"itemList": [],
"isExternal": false,
"path": "/ci/concepts/encryption",
"path": "/ci/concepts/cache-security",
"tags": []
}
],
@ -294,24 +284,14 @@
"path": "/ci/concepts/dte",
"tags": ["distribute-task-execution"]
},
"/ci/concepts/scenarios": {
"id": "scenarios",
"name": "Security Scenarios",
"/ci/concepts/cache-security": {
"id": "cache-security",
"name": "Cache Security",
"description": "",
"file": "nx-cloud/concepts/scenarios",
"file": "nx-cloud/concepts/cache-security",
"itemList": [],
"isExternal": false,
"path": "/ci/concepts/scenarios",
"tags": []
},
"/ci/concepts/encryption": {
"id": "encryption",
"name": "End to End Encryption",
"description": "",
"file": "nx-cloud/concepts/encryption",
"itemList": [],
"isExternal": false,
"path": "/ci/concepts/encryption",
"path": "/ci/concepts/cache-security",
"tags": []
},
"/ci/recipes": {
@ -416,6 +396,16 @@
"isExternal": false,
"path": "/ci/recipes/security/access-tokens",
"tags": []
},
{
"id": "encryption",
"name": "Enable End to End Encryption",
"description": "",
"file": "nx-cloud/recipes/encryption",
"itemList": [],
"isExternal": false,
"path": "/ci/recipes/security/encryption",
"tags": []
}
],
"isExternal": false,
@ -746,6 +736,16 @@
"isExternal": false,
"path": "/ci/recipes/security/access-tokens",
"tags": []
},
{
"id": "encryption",
"name": "Enable End to End Encryption",
"description": "",
"file": "nx-cloud/recipes/encryption",
"itemList": [],
"isExternal": false,
"path": "/ci/recipes/security/encryption",
"tags": []
}
],
"isExternal": false,
@ -772,6 +772,16 @@
"path": "/ci/recipes/security/access-tokens",
"tags": []
},
"/ci/recipes/security/encryption": {
"id": "encryption",
"name": "Enable End to End Encryption",
"description": "",
"file": "nx-cloud/recipes/encryption",
"itemList": [],
"isExternal": false,
"path": "/ci/recipes/security/encryption",
"tags": []
},
"/ci/recipes/source-control-integration": {
"id": "source-control-integration",
"name": "Source Control Integration",

View File

@ -5722,17 +5722,9 @@
"disableCollapsible": false
},
{
"name": "Security Scenarios",
"path": "/ci/concepts/scenarios",
"id": "scenarios",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "End to End Encryption",
"path": "/ci/concepts/encryption",
"id": "encryption",
"name": "Cache Security",
"path": "/ci/concepts/cache-security",
"id": "cache-security",
"isExternal": false,
"children": [],
"disableCollapsible": false
@ -5757,17 +5749,9 @@
"disableCollapsible": false
},
{
"name": "Security Scenarios",
"path": "/ci/concepts/scenarios",
"id": "scenarios",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "End to End Encryption",
"path": "/ci/concepts/encryption",
"id": "encryption",
"name": "Cache Security",
"path": "/ci/concepts/cache-security",
"id": "cache-security",
"isExternal": false,
"children": [],
"disableCollapsible": false
@ -5856,6 +5840,14 @@
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Enable End to End Encryption",
"path": "/ci/recipes/security/encryption",
"id": "encryption",
"isExternal": false,
"children": [],
"disableCollapsible": false
}
],
"disableCollapsible": false
@ -6120,6 +6112,14 @@
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Enable End to End Encryption",
"path": "/ci/recipes/security/encryption",
"id": "encryption",
"isExternal": false,
"children": [],
"disableCollapsible": false
}
],
"disableCollapsible": false
@ -6140,6 +6140,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Enable End to End Encryption",
"path": "/ci/recipes/security/encryption",
"id": "encryption",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Source Control Integration",
"path": "/ci/recipes/source-control-integration",

View File

@ -1714,14 +1714,9 @@
"file": "shared/concepts/improve-worst-case-ci-times"
},
{
"name": "Security Scenarios",
"id": "scenarios",
"file": "nx-cloud/concepts/scenarios"
},
{
"name": "End to End Encryption",
"id": "encryption",
"file": "nx-cloud/concepts/encryption"
"name": "Cache Security",
"id": "cache-security",
"file": "nx-cloud/concepts/cache-security"
}
]
},
@ -1782,6 +1777,11 @@
"name": "Access Tokens",
"id": "access-tokens",
"file": "nx-cloud/recipes/access-tokens"
},
{
"name": "Enable End to End Encryption",
"id": "encryption",
"file": "nx-cloud/recipes/encryption"
}
]
},

View File

@ -0,0 +1,55 @@
# Remote Cache Security
{% callout type="warning" title="Use Caution With Read-Write Tokens" %}
Read-write tokens allow full write access to your remote cache. They should only be used in trusted environments. For instance, open source projects should only use read-write tokens as secrets configured for protected branches (e.g, main). Read-only tokens should be used in all other cases.
{% /callout %}
A cache allows you to reuse work that has already been done, but it also introduces a potential security risk - cache poisoning. A poisoned cache is one where the cached files have been altered in some way by a malicious actor. When a developer or the CI pipeline use that poisoned cache, the task output will be what the malicious actor wants instead of the correct task output.
Nx takes security seriously and has put in place many precautions (we're [SOC 2 compliant](https://security.nx.app)). Listed below are some precautions that you need to take in your own codebase.
## What Data is Sent to the Cache?
Nx does not send your actual code to the remote cache. There are 3 kinds of data that are sent to the Nx Cloud remote cache for each task:
1. A hash of the inputs to the task. There is no way to reconstitute the actual source code files that were used to create a particular hash value.
2. Any files that were created as outputs from a task.
3. The terminal output created when running the task.
If a malicious actor were able to modify the cache and those output files were then executed, that malicious actor could run arbitrary code on developer machines or in CI.
## Recommended Precautions
In order to keep your cache secure, there are a few steps we recommend you take:
### Specify a Read-Only Token in `nx.json`
The [token you specify](/ci/recipes/security/access-tokens) for the `nxCloudAccessToken` property in `nx.json` is visible to anyone who can view your codebase. If this token was a read-write token someone who doesn't even have permission to create a PR could still add entries to the remote cache, which would then be used on other developer's machines and in CI.
### Use a Read-Write Token in CI
If you're in an environment (like an open source project) where you can't trust the contents of a pull request, we recommend restricting the use of a [read-write token](/ci/recipes/security/access-tokens) in CI to just be used on the `main` branch. If you know that everyone who can make a PR is a trusted developer, you can extend that [read-write token](/ci/recipes/security/access-tokens) to also include pull request branches.
### No Need to Revoke Tokens After Employees Leave
When an employee leaves a company, it is standard practice to change all the passwords that they had access to. That is not necessary for Nx Cloud tokens. In order to poison the cache, the former employee would need to have both the read-write token and the current code on the latest commit on the `main` branch. The odds of the employee being able to guess the hash value that will be created for the current commit on the `main` branch are infinitesimally small even after a single commit.
### Skip the Cache When Creating a Deployment Artifact
In order to guarantee that cache poisoning will never affect your end users, [skip the cache](/core-features/cache-task-results#turn-off-or-skip-the-cache) when creating build artifacts that will actually be deployed. Skipping the cache for this one CI run is a very small performance cost, but it gives you 100% confidence that cache poisoning will not be an issue for the end users.
### Do Not Manually Share Your Local Cache
Nx implicitly trusts the local cache which is stored by default in the `.nx/cache` folder. You can change the location of that folder in the `nx.json` file, so it could be tempting to place it on a network drive and easily share your cache with everyone on the company network. However, by doing this you've voided the guarantee of immutability from your cache. If someone has direct access to the cached files, they could directly poison the cache. Nx will automatically detect if a cache entry has been created in your local cache using a different machine and warn you with an [Unknown Local Cache Error](/recipes/troubleshooting/unknown-local-cache). Instead, use Nx Cloud [remote caching](/ci/features/remote-cache).
### Configure End to End Encryption
Nx Cloud guarantees your cache entries will remain immutable - once they've been registered they can't be changed. This is guaranteed because the only way to access the cache is through the Nx Cloud API and we have policies enabled in our cloud storage that specifically disables overwrites and deletions of cached artifacts. But what if a hacker were somehow able make their way into the server holding the cache artifacts? Since you set up [end to end encryption](/ci/recipes/security/encryption), the files they see on disk will be fully encrypted with a key that only exists in your workspace.
### Use An On-Premise Version of Nx Cloud If Needed
If you need to have all cache artifacts on servers that you control, there is an on-premise version of Nx Cloud that you can use as part of the [Enterprise plan](https://nx.app/enterprise).
## Security Decisions
In any security discussion, there is a trade off between convenience and security. It could be that some of these threats do not apply to your organization. If that is the case you could relax some of the security precautions and gain the performance benefits of more task results being stored in the remote cache. Every organization is different and Nx can be adapted to best meet your needs without opening up vulnerabilities. If you would Nx team members to help your organization fine tune your set up, [talk to us about Nx Enterprise](https://nx.app/enterprise).

View File

@ -1,12 +0,0 @@
# End to End Encryption
Data is encrypted both at rest and in transit.
- Every communication with the Nx Cloud API is encrypted in transit, including fetching/storing artifacts.
- When using Nx Public Cloud, the stored metadata is encrypted.
- When using Nx Public Cloud and e2e encryption, stored artifacts are encrypted.
- When using the on-prem version of Nx Cloud, the stored metadata is encrypted if you run MongoDB yourself with encryption on (or if you, for instance, use CosmosDB)
- When using the on-prem version of Nx Cloud, stored artifacts are encrypted using e2e encryption.
You can set the `encryptionKey` property in `nx.json` or set the `NX_CLOUD_ENCRYPTION_KEY` environment variable to
enable the e2e encryption of your artifacts. In this case, the artifacts will be encrypted/decrypted on your machine.

View File

@ -1,45 +0,0 @@
# Security Scenarios
{% callout type="warning" title="Use Caution With Read-Write Tokens" %}
Read-write tokens allow full write access to your remote cache. They should only be used in trusted environments. For instance, open source projects should only use read-write tokens as secrets configured for protected branches (e.g, main). Read-only tokens should be used in all other cases.
{% /callout %}
The following are the three commonly used setups.
## Setup 1: Only main branch has a read-write access token
- A read-only access token is specified in `nx.json`.
- A read-write access token is set using the `NX_CLOUD_ACCESS_TOKEN` env variable in CI only for CI runs on the main/master/development branch.
- The `encryptionKey` value is set in `nx.json`.
In this setup, only builds run against the main branch in CI can upload artifacts to the shared cache that will be used by pull requests and local commands. No one can affect the result of running a command in a pull request or someone else's machine unless they know the read-write token set in CI. This assumes that they have the admin/owner access to the organization in CI. So there is no way to substitute any artifact without knowing the token.
The distributed task execution (DTE) enabled by Nx Cloud works with read-only tokens (so it works for pull requests as well), but the artifacts are scoped to that execution. In other words, artifacts created by a read-only DTE run cannot be accessed by other runs or through the Nx Cloud distributed cache.
The downside of this approach is that pull requests and local commands benefit from the computation run on the main branch, but the main branch doesn't benefit from the computation performed, for instance, on a pull request.
## Setup 2: CI has a read-write access token
- A read-only token is specified in `nx.json`.
- A read-write access token is set using `NX_CLOUD_ACCESS_TOKEN` env variable in CI and is used for pull requests and the main branch.
- The `encryptionKey` value is set in `nx.json`.
This can drastically speed up the CI run against the main branch. The downside is that it is possible for a PR to upload a broken artifact that will be pulled by the main branch and then deployed. To make sure this doesn't happen the CI pipeline can rebuild the artifact, skipping the cache before any deployment. With this, a broken artifact cannot affect the deployment but can temporarily affect what is downloaded on the main branch.
## Setup 3: A read-write access token is stored in `nx.json`
- A read-write token is specified in `nx.json`.
This can speed up CI and local development. The computation performed locally can be shared among developers and CI agents.
## Deciding on a Setup
In small close-sourced projects where developers have similar setups, it's common to have one read-write token shared by all the developers. The token is stored in `nx.json`. Every developer can write artifacts to the cache. Other developers and CI agents can read those values. This assumes a high degree of trust. As with Setup 2, you can still rebuild the artifact from scratch before any deployment.
In large organizations, it's common to have a read token set in `nx.json `and a read-write token set as an `NX_CLOUD_ACCESS_TOKEN` env variable in CI. Developers can benefit from the computation performed on CI but cannot affect the CI execution. Depending on how much you want to isolate the main branch, and what you do with the cached artifacts, you can select between Setup 1 or Setup 2.
## Revoking Access Tokens
Generally, there is no need to revoke access tokens. To affect the computation run against, say, the main branch, you need to know the computation hash of the command you are trying to affect. You can only know the hash by running the command, so you need to have access to the latest HEAD of the main branch.
That's why when a developer loses access to the source code, they lose the ability to know the computation hash. They cannot affect any computation even if they know a read-write access token.

View File

@ -61,4 +61,4 @@ You can also configure the access token by setting the `NX_CLOUD_ACCESS_TOKEN` e
## Using `nx-cloud.env`
You can set locally an environment variable via the `nx-cloud.env` file. Nx Cloud CLI will be looking into this file to load custom configuration like `NX_CLOUD_ACCESS_TOKEN`. These environment variables will take precedence over the configuration in `nx.json`.
You can set an environment variable locally via the `nx-cloud.env` file. Nx Cloud CLI will look in this file to load custom configuration like `NX_CLOUD_ACCESS_TOKEN`. These environment variables will take precedence over the configuration in `nx.json`.

View File

@ -0,0 +1,26 @@
# Enable End to End Encryption
To turn on end to end encryption, specify an encryption key in one of two ways:
- Set the `encryptionKey` property in `nx.json`
- Set the `NX_CLOUD_ENCRYPTION_KEY` environment variable
The key can be any string up to 32 characters long.
Providing an encryption key tells Nx to encrypt task artifacts on your machine before they are sent to the remote cache. Then when cached results are downloaded to your machine they are decrypted before they are used. This ensures that even if someone gained access to the Nx Cloud servers, they wouldn't be able to view your task artifacts.
## Metadata
All the artifacts Nx Cloud uses to replay a task for you are encrypted. That means that even if someone gets access to your Nx Cloud storage bucket, they will not be able to tamper with the files and terminal output that is restored when the task is replayed on your CI or developer's machines.
We also store an un-encrypted version of the terminal output separately that is accessible only to invited members of the workspace on the Nx Cloud web app, so they can see why certain tasks failed. This un-encrypted output is only used in the browser, and not used when replaying the task.
## Summary
Data is encrypted both at rest and in transit.
- Every communication with the Nx Cloud API is encrypted in transit, including fetching/storing artifacts.
- When using Nx Public Cloud, the stored metadata is encrypted.
- When using Nx Public Cloud and e2e encryption, stored artifacts are encrypted.
- When using the on-prem version of Nx Cloud, the stored metadata is encrypted if you run MongoDB yourself with encryption on (or if you, for instance, use CosmosDB)
- When using the on-prem version of Nx Cloud, stored artifacts are encrypted using e2e encryption.

View File

@ -350,7 +350,7 @@ When Circle CI now processes our tasks they'll only take a fraction of the usual
![Circle CI after enabling remote caching](/nx-cloud/tutorial/circle-ci-remote-cache.png)
The commands could be restored from the remote cache because we had run them locally before pushing the changes, thus priming the cache with the results. You can **configure** whether local runs should be read-only or read/write. [Our docs page has more details on various scenarios](/ci/concepts/scenarios) and how to configure them.
The commands could be restored from the remote cache because we had run them locally before pushing the changes, thus priming the cache with the results. You can **configure** whether local runs should be read-only or read/write. [Our docs page has more details on security settings for your remote cache](/ci/concepts/cache-security).
You might also want to learn more about [how to fine-tune caching](/recipes/running-tasks/customizing-inputs) to get even better results.
@ -510,5 +510,5 @@ With this pipeline configuration in place, no matter how large the repository sc
You now have a highly optimized CI configuration that will scale as your repository scales. See what else you can do with Nx Cloud.
- Set up [GitHub PR integration](/ci/recipes/source-control-integration/github) to view Nx Cloud results directly in your PR
- Choose the [security scenario](/ci/concepts/scenarios) that makes sense for your organization
- Choose the [security settings](/ci/concepts/cache-security) that make sense for your organization
- [Record non-Nx commands](/ci/recipes/other/record-commands) and view the results in the Nx Cloud interface

View File

@ -350,7 +350,7 @@ When GitHub Actions now processes our tasks they'll only take a fraction of the
![GitHub Actions after enabling remote caching](/nx-cloud/tutorial/gh-ci-remote-cache.png)
The commands could be restored from the remote cache because we had run them locally before pushing the changes, thus priming the cache with the results. You can **configure** whether local runs should be read-only or read/write. [Our docs page has more details on various scenarios](/ci/concepts/scenarios) and how to configure them.
The commands could be restored from the remote cache because we had run them locally before pushing the changes, thus priming the cache with the results. You can **configure** whether local runs should be read-only or read/write. [Our docs page has more details on security settings for your remote cache](/ci/concepts/cache-security).
You might also want to learn more about [how to fine-tune caching](/recipes/running-tasks/customizing-inputs) to get even better results.
@ -436,5 +436,5 @@ With this pipeline configuration in place, no matter how large the repository sc
You now have a highly optimized CI configuration that will scale as your repository scales. See what else you can do with Nx Cloud.
- Set up [GitHub PR integration](/ci/recipes/source-control-integration/github) to view Nx Cloud results directly in your PR
- Choose the [security scenario](/ci/concepts/scenarios) that makes sense for your organization
- Choose the [security settings](/ci/concepts/cache-security) that make sense for your organization
- [Record non-Nx commands](/ci/recipes/other/record-commands) and view the results in the Nx Cloud interface

View File

@ -87,7 +87,7 @@ Claiming your workspace allows you to
- see stats about your CI runs, cache hits number of agents used for distributing tasks
- enable [source control integrations](/ci/recipes/source-control-integration) to get information embedded in your GitHub, Bitbucket or GitLab PRs
- manage and create access tokens and [adjust access and permission](/ci/concepts/scenarios)
- manage and create access tokens and [adjust access and permission](/ci/concepts/cache-security)
- manage your organization & user permissions for your Nx Cloud workspace
**If you lose this link, you can still connect your workspace to Nx Cloud**. Go to [nx.app](https://nx.app), create an account, and connect your workspace using the access token from `nx.json`.

View File

@ -277,8 +277,7 @@
- [Concepts](/ci/concepts)
- [Reduce Waste in CI](/ci/concepts/reduce-waste)
- [Improve Worst Case CI Times](/ci/concepts/dte)
- [Security Scenarios](/ci/concepts/scenarios)
- [End to End Encryption](/ci/concepts/encryption)
- [Cache Security](/ci/concepts/cache-security)
- [Recipes](/ci/recipes)
- [Set Up CI](/ci/recipes/set-up)
- [Setting up Azure Pipelines](/ci/recipes/set-up/monorepo-ci-azure)
@ -290,6 +289,7 @@
- [Security](/ci/recipes/security)
- [Authenticate with Google Identity](/ci/recipes/security/google-auth)
- [Access Tokens](/ci/recipes/security/access-tokens)
- [Enable End to End Encryption](/ci/recipes/security/encryption)
- [Source Control Integration](/ci/recipes/source-control-integration)
- [Enable GitHub PR Integration](/ci/recipes/source-control-integration/github)
- [Enable Bitbucket Cloud PR Integration](/ci/recipes/source-control-integration/bitbucket-cloud)

View File

@ -23,12 +23,12 @@ const pages: Array<{ title: string; path: string }> = [
path: '/ci/recipes/security/access-tokens',
},
{
title: 'Security Scenarios',
path: '/ci/concepts/scenarios',
title: 'Cache Security',
path: '/ci/concepts/cache-security',
},
{
title: 'End to End Encryption',
path: '/ci/concepts/encryption',
title: 'Enable End to End Encryption',
path: '/ci/recipes/security/encryption',
},
{
title: 'Running Nx Cloud Enterprise',

View File

@ -446,8 +446,10 @@ const nxCloudUrls = {
'/nx-cloud/account': '/ci/recipes/security',
'/nx-cloud/account/google-auth': '/ci/recipes/security/google-auth',
'/nx-cloud/account/access-tokens': '/ci/recipes/security/access-tokens',
'/nx-cloud/account/scenarios': '/ci/concepts/scenarios',
'/nx-cloud/account/encryption': '/ci/concepts/encryption',
'/nx-cloud/account/scenarios': '/ci/concepts/cache-security',
'/nx-cloud/concepts/scenarios': '/ci/concepts/cache-security',
'/nx-cloud/account/encryption': '/ci/recipes/security/encryption',
'/nx-cloud/concepts/encryption': '/ci/recipes/security/encryption',
'/nx-cloud/:path*': '/ci/:path*',
};