Replies: 1 comment
-
This has been shipped in |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Motivation
Turborepo CLI caches build artifacts locally. When linked to a remote cache, Turborepo CLI will upload those artifacts so that they can be downloaded by teammates. The contents of artifacts can range from compiled code to event logs emitted during build. Artifacts uploaded to the remote cache are downloaded and used in local builds or CI builds.
We would like to provide a mechanism to verify integrity and authenticity of the downloaded artifacts: that the artifacts are genuine and have not changed since upload.
Terminologies Used in this RFC
Goals
Non-Goals
Threat Model
Artifacts are blobs of data that are sent to the Remote Cache. These artifacts can be scoped to an individual user or team. If they are scoped to team, then any members of the team have access to download artifacts uploaded by their teammates. For the purposes of this discussion we will assume that artifacts are scoped to a team. In general we can replace team ID with user ID if the scope is for users. Artifacts can not change scopes once uploaded.
The artifact uploads and downloads use user Authentication to the Remote Cache . The artifacts are uploaded and downloaded using the user token, artifact hash, and team ID via
PUT
andGET
requests to/v8/artifacts/:hash?teamId={teamId}
, respectively.Overview of Security Goals:
We would like to guarantee that a user of Turborepo CLI can assert the integrity and authenticity of a downloaded artifact. Any artifact that is downloaded using
GET /v8/artifacts/:hash?teamId={teamId}
is exactly the same artifact that is uploaded toPUT /v8/artifacts/:hash?teamId={teamId}
.Enumeration of Attacks focusing on Code Injection:
PUT
orGET
request for a given hash and teamId.hash
,teamId
, or signature when the artifact is uploaded.Security Non-Goals:
Proposal
Option 1: Single Public-Private Key pair signatures by a key manager service
NOTE: Not specified for this option - Changes to
turbo.json
config or changes to headers in Turborepo CLIPUT
/GET
requests.See other options for related discussion.
We assume that Turborepo CLI is authenticated to request asymmetric key signing and verification from an external key manager. We assume Turborepo CLI has access to the public key but not the private key.
Locally, Turborepo CLI does not have access to the private key to sign the
SHA256(artifact)
digest. Turborepo CLI will request thesignature
from the Key Manager Service and forward thesignature
andartifact
to the Remote Cache. The receiving user will download theartifact
andsignature
. Turborepo CLI will verify thesignature
forSHA256(artifact)
using the public key.There is only a single Public-Private key pair that is used for verifying and creating signatures. Thus, this option doesn't provide a mechanism through Turborepo CLI to revoke Signing access to specific users. The key manager should be responsible for authenticating users` signature requests and revoking access when necessary.
The key manager should not share the private key with individual users. Signatures by the key managers are proof that an authenticated user generated the artifact.
Option 2: Per User Public-Private Key Signatures
We assume that Turborepo CLI is using a strong Public-Private key pairs associated with individual users and certified by the Team. Teams using Turborepo will be responsible for establishing the authenticity of Public Keys.
Turborepo CLI will computes the following for each artifact and append the values as headers to the Remote Cache uploads.
x-artifact-digest
artifact
and JSON stringified metadata (hash
,teamId
,userId
and artifactexpiration
)x-artifact-signature
x-artifact-digest
x-artifact-signature-metadata
Enable Signing on Turborepo CLI
Turborepo will specify an
enableSignatureVerification
flag on theturbo.json
config.Turborepo CLI BYOK: Key Format and Retrieving the Public Private Keys
Turborepo CLI requires:
We assume that the team has an authenticated method to distribute public-private key pairs via some certificate system or similar.
Turborepo CLI can follow a pattern like viper and support decreasing precedence lookups for the user public-private key pair and the list of teammates public keys. Alternatively, these locations can be set and enforced on the
turbo.json
config.Turborepo CLI signs artifacts and verifies
We will refer to the user that generates the artifact as the
sender
, and the user that downloads the artifact as thereceiver
. Assume the sender and receiver have trusted public-private key pairs.==== Sender Steps ====
sender
generatesartifact
withmetadata
metadata
is theJSON.stringify({hash, teamId, userId: "sender", expiration})
digest
fromSHA256(metadata||artifact)
and signs it using thesender
s private key to generatesignature_by_sender(digest)
.x-artifact-digest
:digest
x-artifact-signature
:signature_by_sender(digest)
x-artifact-signature-metadata
:metadata
===== Receiver Steps ====
artifact
and relevant object metadata from the remote cache, which includesdigest
,signature_by_sender(digest)
, andmetadata
digest_new
fromSHA256(metadata||artifact)
and compares it to thedigest
received from the download.digest_new != digest
then Turborepo CLI throws an error.sender
because that is the userId specified in themetadata
.signature_by_sender(digest)
usingdigest
andsender
s public key. If the verification fails Turborepo CLI throws an error.Defense against enumerated attacks
x-artifact-signature
on the downloaded artifact mitigates this attack by asserting that the downloaded artifact is unchanged.enableSignatureVerification
is true or when a downloaded artifact has ax-artifact-signature
header. Thus, any unverified artifact will not be applied by Turborepo CLI.PUT
orGET
request for a given hash and teamId.expiration
field. Turborepo CLI will verify this expiration against the current time to verify downloaded artifacts are still valid. This expiration is configured inturbo.json
and can be on the order of a few days or tuned based on artifact usage frequency.hash
,teamId
or signature when the artifact is uploaded.x-artifact-signature
authenticates thehash
,teamId
. Thus, Turborepo CLI would reject downloaded artifacts that have these tampered fields.Option 3: HMAC-SHA256
We assume that Turborepo CLI is using a strong secret key generated by and shared among the team. Turborepo is written in GO, and there is a standard crypto library implementation of this algorithm that can be used to sign and verify artifacts.
The impact here is that the symmetric key used to sign and verify the artifacts will need to be distributed to teammates using Turborepo CLI.
Turborepo CLI will computes the following for each artifact and append the values as headers to the Remote Cache uploads.
x-artifact-hmac
hash
,teamId
,hmacExpiration
, andhmacKeyVersion
x-artifact-hmac-key-version
x-artifact-hmac
x-artifact-hmac-expiration
x-artifact-hmac
to prevent replaying old artifacts downloadsEnable Signing on Turborepo CLI
Turborepo will specify an
enableHMAC
flag on theturbo.json
config.Turborepo CLI Accepts User provided secret keys
Getting the Key
Yet to be determined what will be easiest for users to expose the secret key from their machine to the Turborepo CLI. Some options include:
turbo.json
path to local file config where keys are storedturbo.json
configured environment variable where key is accessibleTurborepo CLI can follow a pattern like viper and support decreasing precedence lookups for the symmetric key.
Key Format
Turborepo CLI will version secret keys used in signing. This version will be stored on the turbo.json config. The version is semver style
major.minor
. Note that the secret key is not stored on theturbo.json
config, only the version. This version can be attached to metadata on the uploaded artifact. This is useful for processing key rotations and signaling to users that they’re using an expired secret key.Turborepo CLI handles Verification
If an artifact is downloaded and the secret-key version on the artifact metadata is a version used sign the
artifact
, then it must successfully verify. Otherwise, Turborepo CLI should throw an error.If the secret key version of a downloaded artifact is more recent than the version on the turbo.json config than an error is thrown with a message indicating they don’t have most recent key.
If the secret key version of a downloaded artifact is older than the most recent version of the key on the turbo.json we can either:
Defense against enumerated attacks
x-artifact-hmac
on the downloaded artifact mitigates this attack by asserting that the downloaded artifact is unchanged.enableHMAC
is true or when a downloaded artifact has ax-artifact-hmac
header. Thus, any unverified artifact will not be applied by Turborepo CLI.PUT
orGET
request for a given hash and teamId.turbo.json
and can be on the order of a few days or tuned based on artifact usage frequency.hash
,teamId
,hmacExpiration
,hmacKeyVersion
or HMAC tag when the artifact is uploaded.x-artifact-hmac
authenticates thehash
,teamId
,hmacExpiration
andhmacKeyVersion
. Thus Turborepo CLI would reject downloaded artifacts that have these tampered fields.Turborepo CLI pseudocode
Beta Was this translation helpful? Give feedback.
All reactions