7.2 KiB
Overview - Security model
Whenever a PR job is run on Travis, we want to build angular.io
and upload the build artifacts to
a publicly accessible server so that collaborators (developers, designers, authors, etc) can preview
the changes without having to checkout and build the app locally.
This document discusses the security considerations associated with uploading build artifacts as part of the CI setup and serving them publicly.
Security objectives
-
Prevent uploading arbitrary content to our servers. Since there is no restriction on who can submit a PR, we cannot allow any PR's build artifacts to be uploaded.
-
Prevent overwriting other peoples uploaded content. There needs to be a mechanism in place to ensure that the uploaded content does indeed correspond to the PR indicated by its URL.
-
Prevent arbitrary access on the server. Since the PR author has full access over the build artifacts that would be uploaded, we must ensure that the uploaded files will not enable arbitrary access to the server or expose sensitive info.
Issues / Caveats
-
Because the PR author can change the scripts run on CI, any security mechanisms must be immune to such changes.
-
For security reasons, encrypted Travis variables are not available to PRs, so we can't rely on them to implement security.
Implemented approach
In a nutshell
The implemented approach can be broken up to the following sub-tasks:
- Verify which PR the uploaded artifacts correspond to.
- Fetch the PR's metadata, including author and labels.
- Check whether the PR can be automatically verified as "trusted" (based on its author or labels).
- If necessary, update the corresponding PR's verification status.
- Deploy the artifacts to the corresponding PR's directory.
- Prevent overwriting previously deployed artifacts (which ensures that the guarantees established during deployment will remain valid until the artifacts are removed).
- Prevent uploaded files from accessing anything outside their directory.
Implementation details
This section describes how each of the aforementioned sub-tasks is accomplished:
-
Verify which PR the uploaded artifacts correspond to.
We are taking advantage of Travis' JWT addon. By sharing a secret between Travis (which keeps it private but uses it to sign a JWT) and the server (which uses it to verify the authenticity of the JWT), we can accomplish the following: a. Verify that the upload request comes from Travis. b. Determine the PR that these artifacts correspond to (since Travis puts that information into the JWT, without the PR author being able to modify it).
Note: There are currently certain limitation in the implementation of the JWT addon. See the next section for more details.
-
Fetch the PR's metadata, including author and labels.
Once we have securely associated the uploaded artifacts to a PR, we retrieve the PR's metadata - including the author's username and the labels - using the GitHub API. To avoid rate-limit restrictions, we use a Personal Access Token (issued by @mary-poppins).
-
Check whether the PR can be automatically verified as "trusted".
"Trusted" means that we are confident that the build artifacts are suitable for being deployed and publicly accessible on the preview server. There are two ways to check that:
- We can verify that the PR has a pre-determined label, which marks it as "safe for preview". Such a label can only have been added by a maintainer (with the necessary rights) and designates that they have manually verified the PR contents.
- We can verify (again using the GitHub API) the author's membership in one of the
whitelisted/trusted GitHub teams. For this operation, we need a Personal Access Token with the
read:org
scope issued by a user that can "see" the specified GitHub organization. Here too, we use the token by @mary-poppins.
-
If necessary update the corresponding PR's verification status.
Once we have determined whether the PR is considered "trusted", we update its "visibility" (i.e. whether it is publicly accessible or not), based on the new verification status. For example, if a PR was initially considered "not trusted" but the check triggered by a new build determined otherwise, the PR (and all the previously uploaded previews) are made public. It works the same way if a PR has gone from "trusted" to "not trusted".
-
Deploy the artifacts to the corresponding PR's directory.
With the preceding steps, we have verified that the uploaded artifacts have been uploaded by Travis. Additionally, we have determined whether the PR can be trusted to have its previews publicly accessible or whether further verification is necessary. The artifacts will be stored to the PR's directory, but will not be publicly accessible unless the PR has been verified. Essentially, as long as sub-tasks 1, 2 and 3 can be securely accomplished, it is possible to "project" the trust we have in a team's members through the PR and Travis to the build artifacts.
-
Prevent overwriting previously deployed artifacts.
In order to enforce this restriction (and ensure that the deployed artifacts' validity is preserved throughout their "lifetime"), the server that handles the upload (currently a Node.js Express server) rejects uploads that target an existing directory. Note: A PR can contain multiple uploads; one for each SHA that was built on Travis.
-
Prevent uploaded files from accessing anything outside their directory.
Nginx (which is used to serve the uploaded artifacts) has been configured to not follow symlinks outside of the directory where the build artifacts are stored.
Assumptions / Things to keep in mind
-
Each trusted PR author has full control over the content that is uploaded for their PRs. Part of the security model relies on the trustworthiness of these authors.
-
Adding the specified label on a PR and marking it as trusted, gives the author full control over the content that is uploaded for the specific PR (e.g. by pushing more commits to it). The user adding the label is responsible for ensuring that this control is not abused and that the PR is either closed (one way of another) or the access is revoked.
-
If anyone gets access to the
PREVIEW_DEPLOYMENT_TOKEN
(a.k.a.NGBUILDS_IO_KEY
on angular/angular) variable generated for each Travis job, they will be able to impersonate the corresponding PR's author on the preview server for as long as the token is valid (currently 90 mins). Because of this, the value of thePREVIEW_DEPLOYMENT_TOKEN
should not be made publicly accessible (e.g. by printing it on the Travis job log). -
Travis does only allow specific whitelisted property names to be used with the JWT addon. The only known such property at the time is
SAUCE_ACCESS_KEY
(used for integration with SauceLabs). In order to be able to actually use the JWT addon we had to name the encrypted variableSAUCE_ACCESS_KEY
(which we later re-assign toNGBUILDS_IO_KEY
).