Securing Software Supply Chain with Grafeas
Editor's note: This post is written by Kelsey Hightower, Staff Developer Advocate at Google, and Sandra Guo, Product Manager at Google.
Kubernetes has evolved to support increasingly complex classes of applications, enabling the development of two major industry trends: hybrid cloud and microservices. With increasing complexity in production environments, customers—especially enterprises—are demanding better ways to manage their software supply chain with more centralized visibility and control over production deployments.
On October 12th, Google and partners
Grafeas allows build, auditing and compliance tools to exchange comprehensive metadata on container images using a central API. This allows enforcing policies that provide central control over the software supply process. Let’s consider a simple application, PaymentProcessor, that retrieves, processes and updates payment info stored in a database. This application is made up of two containers: a standard ruby container and custom logic. Due to the sensitive nature of the payment data, the developers and DevOps team really want to make sure that the code meets certain security and compliance requirements, with detailed records on the provenance of this code. There are CI/CD stages that validate the quality of the PaymentProcessor release, but there is no easy way to centrally view/manage this information: Grafeas provides an API for customers to centrally manage metadata created by various CI/CD components and enables deploy time policy enforcement through a Kritis implementation. Let’s consider a basic example of how Grafeas can provide deploy time control for the PaymentProcessor app using a demo verification pipeline. Assume that a PaymentProcessor container image has been created and pushed to Google Container Registry. This example uses the gcr.io/exampleApp/PaymentProcessor container for testing. You as the QA engineer want to create an attestation certifying this image for production usage. Instead of trusting an image tag like 0.0.1, which can be reused and point to a different container image later, we can trust the image digest to ensure the attestation links to the full image contents. 1. Set up the environment Generate a signing key: Export the image signer's public key: Create the ‘qa’ AttestationAuthority note via the Grafeas API: Create the Kubernetes ConfigMap for admissions control and store the QA signer's public key: Set up an admissions control webhook to require QA signature during deployment. 2. Attempt to deploy an image without QA attestation Attempt to run the image in paymentProcessor.ymal before it is QA attested: Create the paymentProcessor pod: Notice the paymentProcessor pod was not created and the following error was returned: 3. Create an image signature Assume the image digest is stored in Image-digest.txt, sign the image digest: 4. Upload the signature to the Grafeas API Generate a pgpSignedAttestation occurrence from the signature : Upload the attestation through the Grafeas API: 5. Verify QA attestation during a production deployment Attempt to run the image in paymentProcessor.ymal now that it has the correct attestation in the Grafeas API: With the attestation added the pod will be created as the execution criteria are met. For more detailed information, see this . The demo above showed how you can integrate your software supply chain with Grafeas and gain visibility and control over your production deployments. However, the demo verification pipeline by itself is not a full Kritis implementation. In addition to basic admission control, Kritis provides additional support for workflow enforcement, multi-authority signing, breakglass deployment and more. You can read the
In addition, a hosted alpha implementation of Kritis, called Binary Authorization, is available on Google Container Engine and will be available for broader consumption soon. Google, JFrog, and other partners joined forces to create Grafeas based on our common experiences building secure, large, and complex microservice deployments for internal and enterprise customers. Grafeas is an industry-wide community effort. To learn more about Grafeas and contribute to the project:Example application: PaymentProcessor
Visibility and governance over the PaymentProcessor Code
gpg --quick-generate-key --yes qa\_bob@example.com
gpg --armor --export image.signer@example.com \> ${GPG\_KEY\_ID}.pub
curl -X POST \
"http://127.0.0.1:8080/v1alpha1/projects/image-signing/notes?noteId=qa" \
-d @note.json
kubectl create configmap image-signature-webhook \
--from-file ${GPG\_KEY\_ID}.pub
kubectl get configmap image-signature-webhook -o yaml
kubectl apply -f kubernetes/image-signature-webhook.yaml
kubectl apply -f pods/nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: payment
spec:
containers:
- name: payment
image: "gcr.io/hightowerlabs/payment@sha256:aba48d60ba4410ec921f9d2e8169236c57660d121f9430dc9758d754eec8f887"
kubectl apply -f pods/paymentProcessor.yaml
The "" is invalid: : No matched signatures for container image: gcr.io/hightowerlabs/payment@sha256:aba48d60ba4410ec921f9d2e8169236c57660d121f9430dc9758d754eec8f887
gpg -u qa\_bob@example.com \
--armor \
--clearsign \
--output=signature.gpg \
Image-digest.txt
cat \> occurrence.json \<\<EOF
{
"resourceUrl": "$(cat image-digest.txt)",
"noteName": "projects/image-signing/notes/qa",
"attestation": {
"pgpSignedAttestation": {
"signature": "$(cat signature.gpg)",
"contentType": "application/vnd.gcr.image.url.v1",
"pgpKeyId": "${GPG\_KEY\_ID}"
}
}
}
EOF
curl -X POST \
'http://127.0.0.1:8080/v1alpha1/projects/image-signing/occurrences' \
-d @occurrence.json
kubectl apply -f pods/paymentProcessor.yaml
pod "PaymentProcessor" created
Summary