This document describes best practices for protecting your builds. Building code can refer to different types of operations, such as:
- Optimizing or obfuscating code: For example, the Google open source tool Closure Compiler parses and analyzes JavaScript, removes dead code, and rewrites and minimizes what's left. It also checks the code for common JavaScript pitfalls.
- Compiling code into intermediate code: For example, you can compile Java code
into a Java class file (
.class
), or C++ code into an object file (.obj
). - Compiling code and linking, creating a library or executable file: For example,
compiling C++ code into a Shared Library (
.so
) or a Windows executable file (.exe
). - Packaging code into a distributable or deployable format: Examples include
creating Java WAR (
.war
) files from Java class files, creating a Docker image, or creating a Python built distribution (.whl
).
Depending on the programming language you use and the environment you deploy to, your build might contain different combinations of these operations. For example, a build might package Python code into a built distribution and upload it to an artifact store like Artifact Registry or PyPI so that you can use it as a dependency in Cloud Run functions. You could also containerize the Python code and deploy the container image to Cloud Run or Google Kubernetes Engine.
The practices in this document focus on building code for packaging or deployment to runtime environments rather than compiling code.
Use automated builds
An automated build or scripted build defines all build steps in build script or build configuration, including steps to retrieve source code and steps to build the code. The only manual command, if any, is the command to run the build.
For example, a build script can be:
- A Cloud Build
cloudbuild.yaml
. - A Makefile that you run with the
make
tool. - A GitHub Actions workflow file in YAML format stored in your
.github/workflows/
directory.
Automated builds provide consistency in the build steps. However, it's also important to run builds in a consistent, trusted environment.
Although local builds can be useful for debugging purposes, releasing software from local builds can introduce a lot of security concerns, inconsistencies and inefficiencies into the build process.
- Allowing local builds provides a way for an attacker with malicious intent to modify the build process.
- Inconsistencies in developer local environments and developer practices make it difficult to reproduce builds and diagnose build issues.
Manual builds make the process inefficient by leveraging more infrastructure resources such as compute, storage and networks. In the requirements for the SLSA framework, automated builds are a requirement for SLSA level 1, and using a build service instead of developer environments for builds is a requirement for SLSA level 2.
Cloud Build is the managed build service on Google Cloud. It uses a build config file to provide build steps to Cloud Build. You can configure builds to fetch dependencies, run unit tests, static analyses, and integration tests, and create artifacts with build tools such as Docker, Gradle, Maven, Go, and Python. Cloud Build is fully integrated with other CI/CD services on Google Cloud such as Artifact Registry and Cloud Deploy, as well as runtime environments such as GKE and Cloud Run It also provides integration with major source code management systems such as GitHub and Bitbucket.
Generate build provenance
Build provenance is a collection of verifiable data about a build.
Provenance metadata includes details such as the digests of the built images, the input source locations, the build toolchain, and the build duration.
Generating build provenance helps you to:
- Verify that a built artifact was created from a trusted source location and by a trusted build system.
- Identify code injected from an untrusted source location or build system.
You can use alerting and policy mechanisms to proactively use build provenance data. For example, you can create policies that only allow deployments of code built from verified sources.
For SLSA level 1, build provenance must be available to consumers of the built artifacts. For SLSA level 2, the build provenance data must also be:
- Generated by the build service or readable directly from the build service.
- Verifiable by a consumer for authenticity and integrity. This should be done with a digital signature generated by the service that creates the build provenance data.
For SLSA level 3, the provenance content must also include:
- The entry point of the build definition.
- All build parameters under a user's control.
Cloud Build can generate build provenance for container images that provide SLSA level 3 build assurance. For more information, see Viewing build provenance.
Use an ephemeral build environment
Ephemeral environments are temporary environments that are meant to last for a single build invocation. After the build, the environment is wiped or deleted. Ephemeral builds ensure that the build service and build steps run in an ephemeral environment, such as a container or VM. Instead of reusing an existing build environment, the build service provisions a new environment for each build and then destroys it after the build process is complete.
Ephemeral environments ensure clean builds since there are no residual files or environment settings from previous builds that can interfere with the build process. A non-ephemeral environment provides an opportunity for attackers to inject malicious files and content. An ephemeral environment also reduces maintenance overhead and reduces inconsistencies in the build environment.
Cloud Build sets up a new virtual machine environment for every build and destroys it after the build.
Restrict access to the build service
Follow the security principle of least privilege by granting the minimum required permissions to the build service and build resources. You should also use a non-human identity to run builds and interact with other services on behalf of the build.
If you use Cloud Build:
- Grant the minimum required permissions to members of your organization.
- Customize the permissions for the service account that acts on behalf of Cloud Build so that it only has permissions necessary for your usage. Edit the permissions of the default Cloud Build service account, or consider using a custom service account instead.
- Use the Cloud Build Allowed integrations organization policy to control the external services that are allowed to invoke build triggers.
Place Cloud Build in a service perimeter using VPC Service Controls. The perimeter allows free communication between Google Cloud services within the perimeter, but limits communication across the perimeter based on rules that you specify. The perimeter also mitigates the risk of data exfiltration.
Cloud Build only supports VPC Service Controls for builds that you run in a private pool.
Protect credentials
Builds often include connections to other systems such as version control, artifact stores, and deployment environments. Protecting credentials that you use in your builds helps to prevent unauthorized access to systems in your software supply chain and exfiltration of data.
Avoid storing hard coded credentials directly in version control or in your build configuration. Instead, store credentials in a secure keystore.
In Google Cloud, Secret Manager securely stores API keys, passwords, and other sensitive data. You can configure Cloud Build to use secrets stored in Secret Manager.
Manage your dependencies
The integrity of your applications relies on the integrity of both the code you develop and any dependencies you use. You also need to consider where you publish your dependencies, who has access to read and write to your artifact repositories, and policies for trusted sources of build artifacts that you deploy into your runtime environments.
To learn more about dependency management, see Manage dependencies.
In Cloud Build, you use cloud builders to run commands. Builders are container images with common languages and tools installed in them. You can use public container images from public registries such as Docker Hub, builders provided by Cloud Build, community-contributed builders, and custom builders that you create. You can also use buildpacks as builders, including Google Cloud's buildpacks.
Review the builders that you use in your Cloud Build builds, find out who provides them, and decide if you trust them in your software supply chain. To maintain more control over the code in a builder, you can create custom builders instead of using builders from a public source.
Reduce opportunities to alter the build
There are a variety of other factors that can influence a build, including:
- Builds that run concurrently and are able to influence each other, or a build that persists and impacts a subsequent build.
- Builds that accept user parameters other than the build entry point and the top-level source location.
- Builds that specify dependencies with ranges or dependencies that are
mutable, (for example using an image with the
latest
tag). These approaches create risk for builds to use bad or unwanted versions of dependencies.
The following practices help to mitigate these risks:
- Run each build in an ephemeral environment.
- Avoid running builds with additional parameters so that users cannot influence variables defined in the build scripts.
- Restrict access to the build service and build resources.
- Reference immutable versions of dependencies instead of identifiers such as tags that can point to a different version of the artifact in the future. For more information about dependencies, see Dependency management.
Software Delivery Shield
Software Delivery Shield is a fully-managed, end-to-end software supply chain security solution. It provides a comprehensive and modular set of capabilities and tools across Google Cloud services that developers, DevOps, and security teams can use to improve the security posture of the software supply chain. It displays security insights for built applications in the Cloud Build UI in the Google Cloud console. This includes:
- The SLSA level, which identifies the maturity level of your software supply chain security.
- Vulnerabilities, software bill of materials (SBOM), and Vulnerability Exploitability eXchange (VEX) statements for build artifacts.
- Build provenance, which is a collection of verifiable metadata about a build. It includes details such as the digests of the built images, the input source locations, the build toolchain, build steps, and the build duration.
For instructions on viewing security insights for built applications, see Build an application and view security insights.
What's next
- Learn the best practices to safeguard source code.
- Learn the best practices to safeguard dependencies.
- Learn the best practices to safeguard deployments.