diff --git a/docs/nx-cloud/concepts/cache-security.md b/docs/nx-cloud/concepts/cache-security.md index b68d37f547..c31383c484 100644 --- a/docs/nx-cloud/concepts/cache-security.md +++ b/docs/nx-cloud/concepts/cache-security.md @@ -30,11 +30,16 @@ You can strengthen your workspace security further by revoking all access to the ### Avoid using CI Access Tokens in `nx.json` -While you can [specify a token](/ci/recipes/security/access-tokens) with the `nxCloudAccessToken` property in `nx.json`, this is visible to anyone who can view your codebase. A read-write token would give someone who may not even have permission to create a PR the access to add entries to the remote cache, which would then be used on other developer's machines and in CI. We recommend you restrict CI Access Tokens to CI use only and rely on [personal access tokens](/ci/recipes/security/personal-access-tokens) for local development instead. +Avoid [specifying a token](/ci/recipes/security/access-tokens) with the `nxCloudAccessToken` property in `nx.json` as they're visible to anyone with codebase access. A `read-write` token grants complete cache write access, enabling potential cache poisoning by unauthorized users. Instead, [restrict CI access tokens](/ci/recipes/security/access-tokens) to protected CI environments and use [personal access tokens](/ci/recipes/security/personal-access-tokens) for local development. -### Use a Read-Write Token in CI +### Use Scoped Tokens 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. +We recommend using a [read-write token](/ci/recipes/security/access-tokens#read-write-access) only for protected branches (branches that don't allow direct push). +A `read-write` access token allows reading from and writing to the shared global cache of your workspace. + +In all other branches, we recommend using a [read-only token](/ci/recipes/security/access-tokens#read-only-access). A `read-only` token only allows reading from the shared global cache, while writing is limited to an execution specific isolated cache. +This allows your CI pipelines to share computational work between the [distributed agents](/ci/features/distribute-task-execution). +For workspaces with an enabled [source control integration with Nx Cloud](/ci/recipes/source-control-integration), we can securely scope the isolated cache to the pull request branch, without opening up the possibility of cache poisoning in your trusted environments. Learn more about [access token architecture](/ci/recipes/security/access-tokens#setting-ci-access-tokens). ### No Need to Revoke Tokens After Employees Leave diff --git a/docs/nx-cloud/recipes/access-tokens.md b/docs/nx-cloud/recipes/access-tokens.md index ba3a6a2997..9af64fc4e2 100644 --- a/docs/nx-cloud/recipes/access-tokens.md +++ b/docs/nx-cloud/recipes/access-tokens.md @@ -1,15 +1,16 @@ # Nx CLI and CI Access Tokens -{% youtube src="https://youtu.be/vBokLJ_F8qs" title="Configure CI access tokens" /%} +{% youtube src="" title="Configure CI access tokens" /%} -The permissions and membership define what developers can access on nx.app but they don't affect what happens when you run Nx commands in CI. To manage that, you need to provision CI access tokens in your workspace settings, under the `Access Control` tab. +The permissions and membership define what developers can access on [nx.app](https://cloud.nx.app?utm_source=nx.dev&utm_medium=docs&utm_campaign=nx-cloud-security), but they don't affect what happens when you run Nx commands in CI. To manage that, you need to provision CI access tokens in your workspace settings, under the `Access Control` tab. +Learn more about [cache security best practices](/ci/concepts/cache-security). ![Access Control Settings Page](/nx-cloud/recipes/access-control-settings.avif) ## Access Types {% 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. +The `read-write` tokens allow full write access to your remote cache. They should only be used in trusted environments. {% /callout %} There are currently two (2) types of CI Access Token for Nx Cloud's runner that you can use with your workspace. Both support distributed task execution and allow Nx Cloud to store metadata about runs. @@ -19,21 +20,147 @@ There are currently two (2) types of CI Access Token for Nx Cloud's runner that ### Read Only Access -The `read-only` access tokens will only read from the remote cache. New task results will not be stored in the remote cache, but cached results can be downloaded and replayed for other machines or CI pipelines to use. This option provides the benefit of remote cache hits while restricting machines without proper permissions from adding entries into the remote cache. +The `read-only` access tokens can only read from the global remote cache. Task results produced with this type of access token will be stored in an isolated remote cache accessible _only_ by that specific branch in a CI context, and cannot influence the global shared cache. +The isolated remote cache produced with a `read-only` token is accessible to all machines or agents in the same CI execution, enabling cache sharing during distributed task execution. ### Read & Write Access -The `read-write` access tokens allows task results to be stored in the remote cache for other other machines or CI pipelines to download and replay. +The `read-write` access tokens allow task results to be stored in the remote cache for other machines or CI pipelines to download and replay. This access level should only be used for trusted environments such as protected branches within your CI Pipeline. ## Setting CI Access Tokens -You can configure an access token in CI by setting the `NX_CLOUD_ACCESS_TOKEN` environment variable. `NX_CLOUD_ACCESS_TOKEN` takes precedence over any authentication method in your `nx.json`. +You can configure an access token in CI by setting the `NX_CLOUD_ACCESS_TOKEN` environment variable. -The following example shows how to set the `NX_CLOUD_ACCESS_TOKEN` environment variable in a GitHub Actions workflow. You will need to add the `secrets.NX_CLOUD_ACCESS_TOKEN` secret to your repository based on instructions provided by your CI provider (see [GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) or [GitLab](https://docs.gitlab.com/ee/ci/variables/#define-a-cicd-variable-in-the-ui) instructions). +The `NX_CLOUD_ACCESS_TOKEN` takes precedence over any authentication method in your `nx.json`. -```yml {% fileName=".github/workflows/ci.yml" highlightLines=["29-32"] %} +We recommend setting up a `read-write` token for you protected branches in CI and a `read-only` token for unprotected branches. You can leverage your CI provider's environment variables management to accomplish this. + +### Azure DevOps + +Azure DevOps provides various [mechanisms to limit access to secrets](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/secrets?view=azure-devops#limit-access-to-secret-variables). We'll be using _Variable groups_ in this process, but you can achieve the same result leveraging [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview). + +1. In your project, navigate to Pipelines > Library. + ![Variable group settings page](/nx-cloud/recipes/ado-library-start.avif) +2. Create a new _Variable group_ called _protected_. + - If you already have a variable group for protected environments, we recommend reusing that variable group. +3. Add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-write` token from Nx Cloud. + ![create protected variable group](/nx-cloud/recipes/ado-protected-var-group.avif) +4. In _Pipeline permissions_, add your current pipeline configuration. + ![variable group pipeline permission settings](/nx-cloud/recipes/ado-pipeline-permission.avif) +5. In _Approvals and checks_, add a new _Branch control_ check. + ![variable group branch control settings](/nx-cloud/recipes/ado-add-branch-control.avif) +6. Create the _Branch control_ check with only allowing your protected branches and checking _Verify branch protection_ option. + ![variable group branch control settings](/nx-cloud/recipes/ado-protected-branch.avif) +7. Create another variable group called _unprotected_. +8. Add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-only` token from Nx Cloud. +9. In _Pipeline permissions_, add your current pipeline configuration. +10. In _Approvals and checks_, add a new _Branch control_ check with the `*` wildcard for branches and leaving _Verify branch protection_ unchecked. + ![unprotected variable group settings](/nx-cloud/recipes/ado-unprotected-branch.avif) +11. Now you should see 2 _Variable groups_ for _protected_ and _unprotected_ usage. + ![completed variable group setup](/nx-cloud/recipes/ado-library-end.avif) +12. Update your pipeline to include the 2 variable groups, with conditional access for the _protected_ variable group. + +Example usage: + +```yaml {% fileName="azure-pipelines.yml" highlightLines=["2-4"] %} +variables: + - group: unprotected + - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}: + - group: protected +``` + +{% callout type="check" title="Can't someone change the variable group?" %} +Since we use the _Verify branch protection_ option, CI can only read the variable when running in a protected branch. If a developer tries to edit the pipeline to use the _protected_ variable group, the pipeline will error out since permissions require running in on a protected branch. + +Take caution though, if you allow team members to have direct write access to a protected branch, then they could modify the pipeline to write to the nx cache without having a code review first. +{%/callout %} + +### BitBucket Cloud + +BitBucket Cloud supports setting environment variables per environment called _Deployment variables_. You can read the [official BitBucket Pipelines documentation](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/#Deployment-variables) for more details. + +1. In your repository, navigate to the _Repository settings_ > _Deployment_. +2. Select an environment you have configured for protected branches, or create a new one and protect your primary branches. + - Note: selecting branch protection rules is a premium feature of BitBucket Cloud. + ![Use deployments variables to provide protected environment variable access](/nx-cloud/recipes/bitbucket-deployment-env.avif) +3. Set the environment variable `NX_CLOUD_ACCESS_TOKEN` with the `read-write` token from Nx Cloud. +4. Navigate to the _Repository settings_ > _Repository variables_ tab and set the variable `NX_CLOUD_ACCESS_TOKEN` with the `read-only` token from Nx Cloud. + ![add read-only nx cloud access token to bitbucket](/nx-cloud/recipes/bitbucket-repo-vars.avif) +5. Update the `bitbucket-pipelines.yml` file to include the deployment name mentioned in step 2. + +Example usage: + +```yaml {% fileName="bitbucket-pipelines.yml" highlightLines=[6] %} +pipelines: + branches: + main: + - step: + name: 'main checks' + deployment: Production + ... +``` + +### CircleCI + +Circle CI allows creating _contexts_ and restricting those based on various rules. You can read the [official CircleCI documentation](https://circleci.com/docs/contexts/#restrict-a-context) for more details. + +1. In your organization, navigate to _Organization settings_ > _Contexts_ and create a new context. + - If you already have a context for protected environments, we recommend reusing that context. + ![create a new context for protected environments](/nx-cloud/recipes/circle-new-context.avif) +2. Click on _Add Expression Restriction_ that restricts the context to protected branches only such as only the `main` branch, e.g., `pipeline.git.branch == "main"`. + ![restrict context to protected branches](/nx-cloud/recipes/circle-expression-restriction.avif) +3. Click on _Add Environment Variable_ and add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-write` token from Nx Cloud. +4. Back on the organization home page, navigate to your projects, then view the pipeline settings. +5. Navigate to _Environment Variables_ and click _Add Environment Variable_ and add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-only` token from Nx Cloud. + ![add read-only nx cloud access token to circleci](/nx-cloud/recipes/circle-new-env-var-token.avif) +6. Update your pipeline to include steps where you want to write to the nx cache with the correct contexts. + +Example usage: + +```yaml {% fileName=".circleci/config.yml" highlightLines=["10-19"] %} +jobs: + run-tests-protected: + - ... + run-tests-prs: + - ... + +workflows: + my-workflow: + jobs: + - run-tests-protected: + context: + - protected-branches + filters: + branches: + only: main + - run-tests-prs: + filters: + branches: + ignore: main +``` + +### GitHub Actions + +GitHub allows specifying different secrets for each environment, where an environment can be on a specific branch. +You can read the [official GitHub Actions documentation](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment) for more details. + +1. In your repository, navigate to Settings tab. +2. Click on "Environments" and create an environment for your protected branches. + - Typically, organizations already have some kind of 'release' or 'protected' environments that can be leveraged. + - If you do not have any protected branches, it's recommended to make at least your _default_ branch a protected branch i.e., `main`/`master`. +3. Add a restriction for how the environment will be applied, and apply to all protected branches. + ![Select protected branches for the environment restriction configuration](/nx-cloud/recipes/github-select-protected-branches.avif) +4. Add the `read-write` access token with the name `NX_CLOUD_ACCESS_TOKEN` to your environment. +5. Click the _Secrets and variables_ > _Actions_ tab in the sidebar. +6. Add the `read-only` access token with the name `NX_CLOUD_ACCESS_TOKEN` to the repository secrets. +7. Now you should see 2 secrets where 1 is a part of the protected environment and the other is the default repository secrets. + ![overview of GitHub Action secret configuration settings with environments set](/nx-cloud/recipes/github-secrets-settings.avif) + +Example usage: + +```yaml {% fileName=".github/workflows/ci.yml" highlightLines=["3-4"] %} name: CI -# ... + env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} @@ -43,6 +170,81 @@ jobs: steps: ... ``` +### GitLab + +GitLab allows creating variables scoped to specific environments. You can read the [Official GitLab documentation](https://docs.gitlab.com/ci/environments/#limit-the-environment-scope-of-a-cicd-variable) for more details. + +1. In your project, navigate to _Operate_ > _Environments_ and create a new environment. You do not need to fill out the External Url or GitLab agent. + - Most projects already have a production/protected environments, so we recommend using this one if it's already defined. + ![define gitlab environment for protected branches](/nx-cloud/recipes/gitlab-new-environment.avif) +2. In your project, navigate to _Settings_ > _CI/CD_ tab and expand the _Variables_ section. +3. Click on _Add variable_ and fill in the following information: + - Type: _Variable_ + - Environments: _All_ + - Visibility: _Masked and hidden_ + - Flags: uncheck _Protected variable_ + - Description: "read-only token for nx-cloud" + - Key: `NX_CLOUD_ACCESS_TOKEN` + - Value: Your `read-only` token from Nx Cloud +4. Click _Add variable_. + ![add read-only nx cloud access token to gitlab](/nx-cloud/recipes/gitlab-variable-settings-readonly.avif) +5. Click on _Add variable_ again and fill in the following information: + - Type: _Variable_ + - Environments: Your protected environment created in step 1 + - Visibility: _Masked and hidden_ + - Flags: check _Protected variable_ + - Description: "read-write token for nx-cloud" + - Key: `NX_CLOUD_ACCESS_TOKEN` + - Value: Your `read-write` token from Nx Cloud +6. Click _Add variable_. + ![add read-write nx cloud access token to gitlab](/nx-cloud/recipes/gitlab-variable-settings-readwrite.avif) +7. Now you should see 2 secrets where 1 is a part of the protected & tagged to the environment and the other is not. + ![GitLab project variable configuration screen](/nx-cloud/recipes/gitlab-variable-setting.avif) +8. Update your pipeline to include steps where you want to write to the nx cache with the correct contexts. + +Example usage: + +```yaml {% fileName=".gitlab-ci.yml" highlightLines=["2-3"] %} +: + environment: + name: +``` + +{% callout type="check" title="Can't someone change the step environment?" %} +Since we use the _Protected variable_ flag, CI can only read the variable when running in a protected branch. If a developer tries to edit the steps to run a PR in the environment with the `read-write` token will, then the token will not be populated in CI since their branch is not marked as protected. + +Take caution though, if you allow team members to have direct write access to a protected branch, then they could modify the steps to write to the nx cache without having a code review first. +{%/callout %} + +### Jenkins + +Jenkins configuration can be quite extensive making each Jenkins instance unique. Because of this we can only provide a minimal viable approach, but there can be multiple ways to provide scoped access tokens to your pipelines. The goal is to create two areas within Jenkins, where one is the _protected_ and the other is the _unprotected_. These specifically map to how you deem your branches should have read/write vs read permissions. We recommend making branches that developers cannot directly push to and require a code review to merge to, as the _protected_ branches, and the rest being _unprotected_. + +1. Minimally, this can be achieved via the following Jenkins plugins: + - [Folders](https://plugins.jenkins.io/cloudbees-folder/), [Credentials](https://plugins.jenkins.io/credentials/), [Credentials Binding](https://plugins.jenkins.io/credentials-binding/) +2. Create a folder for the _unprotected_ and _protected_ pipelines. + - The names can be anything that makes sense for your organization, such as _releases_ or _PRs_ etc. +3. Go into the _unprotected_ folder and create a credential for `NX_CLOUD_ACCESS_TOKEN` with the `read-only` token from Nx Cloud. +4. Go into the _protected_ folder and create a credential for `NX_CLOUD_ACCESS_TOKEN` with the `read-write` token from Nx Cloud. +5. Use the credential inside your pipeline `Jenkinsfile` with the Credential Binding plugin. + +Example usage: + +```groovy {% fileName="Jenkinsfile" highlightLines=["6-7"] %} +pipeline { + agent any + stages { + stage('Build') { + steps { + withCredentials([string(credentialsId: 'NX_CLOUD_ACCESS_TOKEN', variable: 'NX_CLOUD_ACCESS_TOKEN')]) { + sh 'echo "nx cloud access token is now set in this context"' + } + } + } + } +} +``` + ### Legacy methods of setting CI Access Tokens #### Using CI Access Tokens in nx.json diff --git a/docs/nx-cloud/recipes/ado-add-branch-control.avif b/docs/nx-cloud/recipes/ado-add-branch-control.avif new file mode 100644 index 0000000000..812d1a0f75 Binary files /dev/null and b/docs/nx-cloud/recipes/ado-add-branch-control.avif differ diff --git a/docs/nx-cloud/recipes/ado-library-end.avif b/docs/nx-cloud/recipes/ado-library-end.avif new file mode 100644 index 0000000000..9fba643a74 Binary files /dev/null and b/docs/nx-cloud/recipes/ado-library-end.avif differ diff --git a/docs/nx-cloud/recipes/ado-library-start.avif b/docs/nx-cloud/recipes/ado-library-start.avif new file mode 100644 index 0000000000..c648303e4d Binary files /dev/null and b/docs/nx-cloud/recipes/ado-library-start.avif differ diff --git a/docs/nx-cloud/recipes/ado-pipeline-permission.avif b/docs/nx-cloud/recipes/ado-pipeline-permission.avif new file mode 100644 index 0000000000..a85cd13032 Binary files /dev/null and b/docs/nx-cloud/recipes/ado-pipeline-permission.avif differ diff --git a/docs/nx-cloud/recipes/ado-protected-branch.avif b/docs/nx-cloud/recipes/ado-protected-branch.avif new file mode 100644 index 0000000000..19d7a7d599 Binary files /dev/null and b/docs/nx-cloud/recipes/ado-protected-branch.avif differ diff --git a/docs/nx-cloud/recipes/ado-protected-var-group.avif b/docs/nx-cloud/recipes/ado-protected-var-group.avif new file mode 100644 index 0000000000..9814ae418f Binary files /dev/null and b/docs/nx-cloud/recipes/ado-protected-var-group.avif differ diff --git a/docs/nx-cloud/recipes/ado-unprotected-branch.avif b/docs/nx-cloud/recipes/ado-unprotected-branch.avif new file mode 100644 index 0000000000..edd6fae985 Binary files /dev/null and b/docs/nx-cloud/recipes/ado-unprotected-branch.avif differ diff --git a/docs/nx-cloud/recipes/bitbucket-deployment-env.avif b/docs/nx-cloud/recipes/bitbucket-deployment-env.avif new file mode 100644 index 0000000000..996e55a17f Binary files /dev/null and b/docs/nx-cloud/recipes/bitbucket-deployment-env.avif differ diff --git a/docs/nx-cloud/recipes/bitbucket-repo-vars.avif b/docs/nx-cloud/recipes/bitbucket-repo-vars.avif new file mode 100644 index 0000000000..19a5f24704 Binary files /dev/null and b/docs/nx-cloud/recipes/bitbucket-repo-vars.avif differ diff --git a/docs/nx-cloud/recipes/circle-expression-restriction.avif b/docs/nx-cloud/recipes/circle-expression-restriction.avif new file mode 100644 index 0000000000..2774b84305 Binary files /dev/null and b/docs/nx-cloud/recipes/circle-expression-restriction.avif differ diff --git a/docs/nx-cloud/recipes/circle-new-context.avif b/docs/nx-cloud/recipes/circle-new-context.avif new file mode 100644 index 0000000000..c37365e735 Binary files /dev/null and b/docs/nx-cloud/recipes/circle-new-context.avif differ diff --git a/docs/nx-cloud/recipes/circle-new-env-var-token.avif b/docs/nx-cloud/recipes/circle-new-env-var-token.avif new file mode 100644 index 0000000000..d4a34be110 Binary files /dev/null and b/docs/nx-cloud/recipes/circle-new-env-var-token.avif differ diff --git a/docs/nx-cloud/recipes/circle-new-env-var.avif b/docs/nx-cloud/recipes/circle-new-env-var.avif new file mode 100644 index 0000000000..97be13923c Binary files /dev/null and b/docs/nx-cloud/recipes/circle-new-env-var.avif differ diff --git a/docs/nx-cloud/recipes/github-secrets-settings.avif b/docs/nx-cloud/recipes/github-secrets-settings.avif new file mode 100644 index 0000000000..3220ffb255 Binary files /dev/null and b/docs/nx-cloud/recipes/github-secrets-settings.avif differ diff --git a/docs/nx-cloud/recipes/github-select-protected-branches.avif b/docs/nx-cloud/recipes/github-select-protected-branches.avif new file mode 100644 index 0000000000..7d5820f504 Binary files /dev/null and b/docs/nx-cloud/recipes/github-select-protected-branches.avif differ diff --git a/docs/nx-cloud/recipes/github-settings-overview.avif b/docs/nx-cloud/recipes/github-settings-overview.avif new file mode 100644 index 0000000000..c7473dea5d Binary files /dev/null and b/docs/nx-cloud/recipes/github-settings-overview.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-new-environment.avif b/docs/nx-cloud/recipes/gitlab-new-environment.avif new file mode 100644 index 0000000000..c6f152784d Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-new-environment.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-project-nav.avif b/docs/nx-cloud/recipes/gitlab-project-nav.avif new file mode 100644 index 0000000000..f8ce643d2c Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-project-nav.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-settings-nav.avif b/docs/nx-cloud/recipes/gitlab-settings-nav.avif new file mode 100644 index 0000000000..d758738469 Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-settings-nav.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-variable-setting.avif b/docs/nx-cloud/recipes/gitlab-variable-setting.avif new file mode 100644 index 0000000000..4c224d2e1e Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-variable-setting.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-variable-settings-readonly.avif b/docs/nx-cloud/recipes/gitlab-variable-settings-readonly.avif new file mode 100644 index 0000000000..e7c3b02fd2 Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-variable-settings-readonly.avif differ diff --git a/docs/nx-cloud/recipes/gitlab-variable-settings-readwrite.avif b/docs/nx-cloud/recipes/gitlab-variable-settings-readwrite.avif new file mode 100644 index 0000000000..69ee986fcf Binary files /dev/null and b/docs/nx-cloud/recipes/gitlab-variable-settings-readwrite.avif differ