This project sets up JFrog Artifactory repositories, groups and permissions using the Atlassian Terraform Provider. It is intended for use with docker repositories, but the patterns can be extended to any type of repo.
Production environments should be tamper free and deployments fully traceable. A critical part of this is a secure image and binary promotion pipeline. With a single repo accessible across all environments, the possibility of a mistaken or malicious deployment increases. Without safety controls these things can easily happen. Promotion pipelines are a pattern that can help reduce accidental deployments, and interference in scure deployments. By controlling access to repos, and promoting images through environment specific repositories we can go some way in achieving the goal of secure, traceable production artefacts. We need to own our deployments which means we need to know what is deployed in our environments. Repositories used correctly can provide these safety guard rails. By ensuring that all software deployments and releases come via repositories we can:
- stop random things being downloaded from the internet and deployed (or pulled in via scripts)
- show lineage of where things came from
- give confidence of stable versions (never use latest)
- enable regular scanning for vulnerabilities and licenses
- promoting immutable images through repositories means we know the built image is what we think it is
The following principles are at the base of this project:
- qa should not test dev images
- non-tested images should not be staged
- non-staged, non-tested or dev images should not go to prod
- all images come via Artifactory (or some other repository)
Assuming repositories are the storage location for images and binaries, it is important to control who can create, read, update and delete. Permissions can also be used to control the promotion pipelines and so are useful for process flow, lineage and therefore audit.
The following repos are created:
docker-dev-virtual
- A virtual repo includingdocker-dev-local
anddocker-virtual
. The dev environment gets all images from here. No other cluster can access.docker-dev-local
- for the newly built image.docker-stg-virtual
- A virtual repo includingdocker-stg-local
anddocker-virtual
. Images can only be promoted here from dev by thepromote-to-docker-stg-local
. The stg environment gets all images from here. No other cluster can access.docker-stg-local
- once tested in dev, promote the image to stg.docker-prod-virtual
- A virtual repo includingdocker-prod-local
anddocker-virtual
. Images can only be promoted here from stg by thepromote-to-docker-prod-local
. The prod environment gets all images from here. No other cluster can access.docker-prod-local
- once testing in stg, promote the image to prod.docker-remote
- proxy repo for docker hubdocker-virtual
- a single point of entry for all the repos, CI tools or developer connect to this repo to avoid multiple connections to internal repos.
Note that not all images will be promoted up to prod. The dev
and stg
repos are set up to prune more than 10 images.
The following groups are created:
read-docker-all
- group with permissions to read all docker repos, used by CI tool and developersupload-docker-dev-local
- group with permission to upload to docker-dev-local, used by the CI tool to deploy buildsdownload-docker-remote
- group with permissions to download and cache images from docker hubread-docker-dev-virtual
- group with permissions to readdocker-dev-virtual
onlyread-docker-stg-virtual
- group with permissions to readdocker-stg-virtual
onlyread-docker-prod-virtual
- group with permissions to read docker-prod-virtual onlypromote-to-docker-stg-local
- group with permissions to readdocker-dev-local
and deploy todocker-stg-local
, no delete means that no image tag update is possiblepromote-to-docker-prod-local
- group with permissions to readdocker-stg-local
and deploy todocker-prod-local
, no delete means that no image tag update is possible
The permissions mean that the only way to add an image to the pipeline is through the deploy-docker-dev-local
group, there is no other method, giving us assurance of the pipeline integrity.
Terraform 11 jq docker jfrog cli
From the root directory:
export ARTIFACTORY_ACCESS_TOKEN=""
export ARTIFACTORY_URL=""
terraform init
terraform plan
terraform apply
I'm assuming that the automation will be uploading/promoting artefacts between environments, once approved by whatever process is in place. JFrog can use a temporary user assigned to a group, and then assign a token for this purpose, obviating the need to create fake users. For usage look in the test file. here is a snippet:
# this will be the assigned temporary username
username=username=read-docker-dev-virtual
# is a member of these groups, that already have the required permissions
scope=scope=member-of-groups:read-docker-dev-virtual
# get the token
get_access_token () {
curl -u ${user_token} -d ${username} -d ${scope} -X POST ${token_url} | jq -r '.access_token'
}
# wrap inside a variable
access_token=$(get_access_token)
# upload the image to docker-dev-local
jfrog rt docker-push ${virtual_repo}/busybox:${docker_tag} docker-dev-local --access-token=${access_token} --url=${jfrog_artifactory}
The tests are kind of brute force. I wanted testing but wasn't prepared to spend a day tidying them up. Tests check the following:
deploy-docker-dev-local
- group can tag and deploy an image todocker-dev-local
via the virtual repo, is unable to promote todocker-stg-local
promote-to-docker-stg-local
- group can promote a tag todocker-stg-local
, cannot promote todocker-prod-local
promote-to-docker-prod-local
- group can promote a tag todocker-prod-local
, cannot promote todocker-prod-local
read-docker-all
- group can read from all reposread-docker-dev-virtual
can only read fromdocker-dev-virtual
, error thrown for other reposread-docker-stg-virtual
can only read fromdocker-stg-virtual
, error thrown for other reposread-docker-prod-virtual
can only read fromdocker-prod-virtual
, error thrown for other repos
This uses the test file. Pass in your JFrog url and then the token when asked (the token you will need to get from the JFrog UI)
# for my user token I use the format username:apiToken
./test-permissions.sh yoururl.jfrog.io
'user token : ' user_token
Atlassian JFrog Terraform Provider JFrog talk on pipeline promotion JFrog CLI Promoting docker images API
## TODO Upgrade to Terraform 0.13