In this blog you’ll learn how to set up vulnerability scanning for all of your container images using Azure Container Registry Tasks and Aqua’s microscanner. By the end, you’ll know how to create a relatively complex workflow which prevents your images getting pushed to a container registry if they have vulnerabilities found during build time, preventing the distribution of them entirely.
This is generic, so you can bring whatever image scanner you prefer. I thought this project was pretty dope and there are some great folks over at Aqua, so I wanted to give it a shot.
Create the ACR Task
Alright, go here to see the code - this is ACR Task code you want to play with. Let’s dissect it and then walk through the full set up:
version: v1.0.0
secrets:
- id: key
keyvault: https://ehotingerwuskv.vault.azure.net/secrets/aquakey
steps:
- build: . -t {{.Run.Registry}}/image:{{.Run.ID}}
# Create a new Dockerfile with the scanner added to the previous image.
- cmd: |
bash -c 'echo "FROM {{.Run.Registry}}/image:{{.Run.ID}}
ADD https://get.aquasec.com/microscanner /
RUN chmod +x /microscanner
RUN /microscanner {{.Secrets.key}}" > scan.Dockerfile'
# Scan the image using the Dockerfile I created.
- build: . -f scan.Dockerfile -t scanned
# Only push the image if the scan was successful.
- push: ["{{.Run.Registry}}/image:{{.Run.ID}}"]
We do an initial build of our image, in my case it’s a boring hello world Go application. Next, we create a brand new Dockerfile on the fly and add in the Aqua microscanner. The Aqua microscanner requires a key to use it, so we use {{.Secrets.key}}
to reference our key from Azure Key Vault.
Lastly, we do a build based on that dynamically generated Dockerfile - this runs the Aqua scanner. Note: be sure to read the manual on their GitHub about the scanner - you need to make sure you have the right certs and access to a shell – so you’ll have to make sure your base image is sufficient or craft it slightly differently than mine.
If the build fails, our Task ends - so our image won’t get pushed in the final step. This blocks the distribution of our bad image entirely so nobody gets bad images. Hooray!
Registering Aqua’s microscanner
You can run Aqua’s microscanner locally and do registration from there:
docker run --rm -it aquasec/microscanner --register <email address>
Check your email - you now have a key. We’re going to store it in Azure Key Vault.
Set up Azure Key Vault
First, set up the key vault:
az keyvault create --name ehotingerwuskv --resource-group ehotingerwusrg --location westus
Then create a secret in the key vault based off your previous email:
az keyvault secret set \
--name aquakey \
--value "<your email token>" \
--vault-name ehotingerwuskv
Set up identities for Azure Container Registry
You can read this post for more info about identities and how to use them with Azure Container Registry, but these are the simple steps for setting up a user-assigned identity.
Create the identity:
az identity create --resource-group ehotingerwusrg --name aquaidentity
Get the resourceID and principalID of the identity for later:
resourceID=$(az identity show --resource-group ehotingerwusrg --name aquaidentity --query id --output tsv)
principalID=$(az identity show --resource-group ehotingerwusrg --name aquaidentity --query principalId --output tsv)
Give the identity access to the Azure Key Vault:
az keyvault set-policy --name ehotingerwuskv --resource-group ehotingerwusrg --object-id $principalID --secret-permissions get
Give the identity push access to the Azure Container Registry:
az role assignment create --role acrpush --resource-group ehotingerwusrg --assignee $principalID
Create a Task using the user-assigned identity
Now all you have to do is create a Task based on the above yaml using the identity you set up.
az acr task create \
--registry ehotingerwus \
--name aqua-scan \
--context https://github.com/ehotinger/scratch.git \
--file aqua-scan.yaml \
--commit-trigger-enabled false \
--pull-request-trigger-enabled false \
--assign-identity $resourceID
And then you can trigger a run of it manually to test it out:
az acr task run --name aqua-scan --registry ehotingerwus
My output is something like this, no issues at all so my image gets successfully published to my Azure Container Registry!
{
"scan_started": {
"seconds": 1563585594,
"nanos": 250683665
},
"scan_duration": 1,
"digest": "<blah>",
"os": "alpine",
"version": "3.10.1",
"image_assurance_results": {
"checks_performed": [
{
"policy_id": 1,
"policy_name": "Default",
"control": "max_severity",
"maximum_severity_allowed": "high"
}
]
},
"vulnerability_summary": {
"total": 0,
"high": 0,
"medium": 0,
"low": 0,
"negligible": 0,
"sensitive": 0,
"malware": 0
},
"scan_options": {},
"initiating_user": "token",
"data_date": 1563493655,
"changed_result": false,
"function_metadata": {}
}
Removing intermediate container b4b7a1a6738a
---> b8b25a165090
Successfully built b8b25a165090
Successfully tagged scanned:latest
2019/07/20 01:19:57 Successfully executed container: acb_step_2
2019/07/20 01:19:57 Executing step ID: acb_step_3. Timeout(sec): 600, Working directory: '', Network: 'acb_default_network'
2019/07/20 01:19:57 Pushing image: ehotingerwus.azurecr.io/image:cf16, attempt 1
The push refers to repository [ehotingerwus.azurecr.io/image]
...
Conclusion
Hope this gives you some more ideas for crazy experiments. Be sure to check out all the linked GitHub repositories and make issues/feature requests.
For automatic security patching, be sure to check out ACR Tasks Base Image Updates as well.
Lastly, if you haven’t seen my first post about ACR tasks and autopurge, be sure to check that one out too.