Basic OCICrypt KeyProvider for KMS (GCP only for now, AWS, Azure would be pretty easy to add).
This repo includes a prebuilt and customizeable keyprovider which can be used to encrypt OCI Containers.
OCICrypt includes specifications to encrypt an OCI Container image and within that, the keyprovider protocol allows wrapping of the actual key used to encrypt the layer to an external binary.
The binary in this question accepts a keyprovider request and inturn wraps the layer symmetric encryption key using a hosted KMS key.
Basically, KMS wraps the symmetric key that is used to encrypt the layer itself.
This sample is based off of the simple-oci-keyprovider repo which demonstrates the protocol involved.
For more information, see
- Advancing container image security with encrypted container images
- Enabling advanced key usage and management in encrypted container images
- Container Image Encryption & Decryption in the CoCo project
Note, for KMS and other systems, you can also use built in PKCS11 support. If you are interested in using a Trusted Platform Module
as the root encryption source, see Container Signing with Cosign and TPM PKCS-11 and PKCS 11 Samples in Go using SoftHSM.
Anyway, this repo shows basic OCI container encryption and then an example with GCP KMS as the key wrapping provider.
Showing how this works involves a number of steps so its not that much of a quickstart but once its setup, you can skip to the "encrypt/decrypt" section below.
install
Create a KMS Key to use on GCP
export PROJECT_ID=`gcloud config get-value core/project`
export PROJECT_NUMBER=`gcloud projects describe $PROJECT_ID --format='value(projectNumber)'`
export GCLOUD_USER=`gcloud config get-value core/account`
gcloud auth application-default login
gcloud kms keyrings create ocikeyring --location=global
gcloud kms keys create key1 --keyring=ocikeyring --location=global --purpose=encryption
# this is unnecessary since you should already have permissions
gcloud kms keys add-iam-policy-binding key1 \
--keyring=ocikeyring --location=global \
--member="user:$GCLOUD_USER" --role=roles/cloudkms.cryptoKeyEncrypterDecrypter
(or download the binary from the "releases" page)
cd plugin
go build -o kms_oci_crypt .
- Test binary provider
Edit example/ocicrypt.json
and enter the full path to the binary:
{
"key-providers": {
"kmscrypt": {
"cmd": {
"path": "/full/path/to/kms_oci_crypt",
"args": []
}
}
}
}
Run a local docker registry just to test (vs docker-daemon)
cd example
docker run -p 5000:5000 -v `pwd`/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/localhost.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/localhost.key docker.io/registry:2
Build a small app and push to local reg
cd example
docker build -t app:server .
export SSL_CERT_FILE=certs/tls-ca-chain.pem
skopeo copy docker-daemon:app:server docker://localhost:5000/app:server
In a new shell, specify the path to the config file
export OCICRYPT_KEYPROVIDER_CONFIG=/full/path/to/ocicrypt.json
Then encrypt the last layer
export PROJECT_ID=`gcloud config get-value core/project`
skopeo copy --encrypt-layer=-1 \
--encryption-key=provider:kmscrypt:gcpkms://projects/$PROJECT_ID/locations/global/keyRings/ocikeyring/cryptoKeys/key1 \
docker://localhost:5000/app:server docker://localhost:5000/app:encrypted
The last layer on the image shjould be encrypted
skopeo inspect docker://localhost:5000/app:encrypted
export PROJECT_ID=`gcloud config get-value core/project`
skopeo copy \
--decryption-key=provider:kmscrypt:gcpkms://projects/$PROJECT_ID/locations/global/keyRings/ocikeyring/cryptoKeys/key1 \
docker://localhost:5000/app:encrypted docker://localhost:5000/app:decrypted
Inspect the decrypted image
skopeo inspect docker://localhost:5000/app:decrypted
Finally, you can specify the path GCP Application Default Credentials
file by setting the startup argument --adc
. You can use this setting to direct the GCP encryption to use Workload Identity Federation
{
"key-providers": {
"kmscrypt": {
"cmd": {
"path": "/path/to/kms_oci_crypt",
"args": [
"--adc=/path/to/application_default_credentials.json"
]
}
}
}
}
Included in this repo is a grpc service which you can use as the key provider.
Basically, its the same as calling the binary except that it calls a gRPC server you run separately.
Note, the existing implementation does not use TLS!. You would definitely want to secure access to this service.
To use, start the server
cd grpc
go run server.go
set the OCICRYPT_KEYPROVIDER_CONFIG
file to use
{
"key-providers": {
"kmscrypt": {
"cmd": {
"path": "/path/to/kms_oci_crypt",
"args": []
}
},
"grpc-keyprovider": {
"grpc": "localhost:50051"
}
}
}
Finally invoke the endpoints (note provider:grpc-keyprovider
is used below)
cd example/
export SSL_CERT_FILE=certs/tls-ca-chain.pem
skopeo copy --encrypt-layer -1 \
--encryption-key=provider:grpc-keyprovider:gcpkms://projects/$PROJECT_ID/locations/global/keyRings/ocikeyring/cryptoKeys/key1 \
docker://localhost:5000/app:server docker://localhost:5000/app:encrypted
skopeo copy --dest-tls-verify=false \
--decryption-key=provider:grpc-keyprovider:gcpkms://projects/$PROJECT_ID/locations/global/keyRings/ocikeyring/cryptoKeys/key1 \
docker://localhost:5000/app:encrypted docker://localhost:5000/app:decrypted