From 1f372e21c62381bfd091e5a3c1b5563b3b44ed46 Mon Sep 17 00:00:00 2001 From: Sushant Adhikari Date: Wed, 11 Sep 2024 14:53:47 +1000 Subject: [PATCH 1/4] Proposal for Tag and digest co-existing --- docs/proposals/Tag-Digest-CoExist.md | 88 ++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/proposals/Tag-Digest-CoExist.md diff --git a/docs/proposals/Tag-Digest-CoExist.md b/docs/proposals/Tag-Digest-CoExist.md new file mode 100644 index 000000000..a7f07ef22 --- /dev/null +++ b/docs/proposals/Tag-Digest-CoExist.md @@ -0,0 +1,88 @@ +# Ratify Mutation: mutual existence of tags and digest. + +## Problems + +Current User scenarios: +1. I want to see which version of my image is deployed but I can only see the digest in the pod image. + 1. This results in the engineer’s time being wasted on manually mapping between the image digest and the version from the image repository. + 1. All my observability dashboards rely on tags but now I need to manually know which tags belong to which digests and somehow also make my dashboards map between those. This is a big hassle. + +Other user scenarios that encompass tag mutations NOT related to the current issue which users might want: +1. In case of the pod spec already having both tag and digest, I want to be able to mutate the tag regardless and update the digest accordingly instead of leaving the digest in place. + +## Solution Overview + +The current solution has been chosen on the basis that Ratify is only meant to mutate from tags to digest as tags are mutable and digests are the ultimate source of truth. This solution is also prepared with the community recommendation of using digests instead of tags in mind with digests being the ultimate source of truth for artifact verification. + +However with the digest-only approach having altercations with broader software engineers NOT focused towards security, embedding digests alongside pre-existing tags in the K8s object spec during mutation as a debug-friendly and engineer friendly way forward seems feasible. As the end container orchestration framework such as `containerd` and ultimately `runc` still continue to rely on only the mutated digest to create containers, engineers on the other hand can rely on the pre-existing and untouched tag in the deployed object (Deployment, Pod, StatefulSet etc)’s image spec to know their source of truth for debugging purposes. + +As discussed in the corresponding [Github issue](https://github.com/ratify-project/ratify/issues/1657), having both tag & digest (`:@`) is NOT a recommended option but retains status for backward compatibility, new options with default configuration adhering to this shall be discussed in the design section. + +## Solution Design and Configurations / Proposed Changes + +This section discusses the various additions to the current Ratify Mutator and the corresponding Helm configuration that can help resolve this problem. We also discuss features that the mutator could incorporate to facilitate other additional problems but is a topic of further discussion and debate not within the realm and scope of the problem at hand. + +The solution proposes tweaks to the following sections to fix the problem at hand: +1. The helm chart shall be changed to add the corresponding new configs facilitating these new features for mutation. +1. The new configs shall be respectively passed to /app/ratify serve called through the deployment.yaml file to then handle the additional configs. +1. These configs then trickle down to the corresponding mutation code block to handle the mutations according to those config. + +### Configurations + +A new config block shall be added in the helm chart’s `values.yaml` which will be used respectively. + +This feature will be available through the new `provider.mutation` config block which will make the `provider.enableMutation` option obsolete. A new boolean sub-config of `provider.mutation.enable` will be added to facilitate this existing feature. + +The following sub-options will be added to incorporate additional configuration during mutation. + +| Option Name | Implemented / Designed in the current solution? | Summary | Incoming Spec Condition | Upstream calls for Subject Descriptor? | Default Option | +| ----------- | ----------------------------------------------- | ------- | ----------------------- | -------------------------------------- | -------------- | +| retainMutatedTag | Y | Retain the pre-existing tag during mutation / Do not strip the tag away if both tag & digest pre-exists |Contains Tag, Does not contain Digest / Contains Tag, Contains Digest | Y / N | false | + +The options can work in conjunction to provide the required mutation output. +Here, + +`Latest` tag’s digest = `xxxx` +`v1.2.4` tag’s digest = `yyyy` + + +| Config | Input | Output | +| ------ | ----- | ------ | +| retainMutatedTag: false | docker.io/nginx | docker.io/nginx@sha256:xxxxx | +| | docker.io/nginx:latest | docker.io/nginx@sha256:xxxxx | +| | docker.io/nginx:v1.2.4 | docker.io/nginx@sha256:yyyy | +| | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx@sha256:xxxx | +| | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx@sha256:yyyy | +| retainMutatedTag: true | docker.io/nginx | docker.io/nginx:latest@sha256:xxxx | +| | docker.io/nginx:v1.2.4 | docker.io/nginx:v1.2.4@sha256:yyyy | +| | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx:latest@sha256:xxxx | +| | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx:v1.2.4@sha256:yyyy | + +### Implementation + +The `retainMutatedTag` config will be implemented to retain the tag in the resulting spec image. The default option for this config will be `false` to keep supporting the existing config parameter. + +Options of provider.mutation.enable and provider.mutation.retainMutatedTag shall be added into Helm. +Example: + +``` +provider: + tls: + crt: "" +... + mutation: + enable: true + retainMutatedTag: false // (default) +``` + +The `retainMutatedTag` option will be available for anyone wanting to control if they want to completely remove tags (the default) or have both tags + digest in the resulting output. + +## Performance Impact +The solution should have very little performance impact considering addition of code will not have any network connectivity related feature. Addition of code mostly should adhere to if-else clauses and other small regex additions. + +## Security Considerations +As the change is purely beautification in nature, no security impact could be thought of. +As long as research suggests all major container orchestration frameworks (docker, podman, etc) support the `tag@digest`, however it’s not guaranteed that it’ll work with other smaller frameworks where this hasn’t yet been implemented. + +## Backward Compatibility +The added config won’t be backward compatible if mutation has been disabled, i.e `enableMutation:false` in the helm chart. This means that a new `provider.mutation.enable` will need to be added in the updated helm charts. From 8b061fdcc9e1947584e3aafe984329f2d1d6df0f Mon Sep 17 00:00:00 2001 From: Sushant Adhikari Date: Wed, 11 Sep 2024 15:05:29 +1000 Subject: [PATCH 2/4] Code syntax fix for ratify through deployment.yaml --- docs/proposals/Tag-Digest-CoExist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposals/Tag-Digest-CoExist.md b/docs/proposals/Tag-Digest-CoExist.md index a7f07ef22..5081e68e2 100644 --- a/docs/proposals/Tag-Digest-CoExist.md +++ b/docs/proposals/Tag-Digest-CoExist.md @@ -24,7 +24,7 @@ This section discusses the various additions to the current Ratify Mutator and t The solution proposes tweaks to the following sections to fix the problem at hand: 1. The helm chart shall be changed to add the corresponding new configs facilitating these new features for mutation. -1. The new configs shall be respectively passed to /app/ratify serve called through the deployment.yaml file to then handle the additional configs. +1. The new configs shall be respectively passed to `/app/ratify serve` called through the `deployment.yaml` file to then handle the additional configs. 1. These configs then trickle down to the corresponding mutation code block to handle the mutations according to those config. ### Configurations From f57fcaa28f92c53b0b9b3fcda268a058f463e967 Mon Sep 17 00:00:00 2001 From: Sushant Adhikari Date: Thu, 12 Sep 2024 09:29:31 +1000 Subject: [PATCH 3/4] Update docs to fix PR comments. --- docs/proposals/Tag-Digest-CoExist.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/proposals/Tag-Digest-CoExist.md b/docs/proposals/Tag-Digest-CoExist.md index 5081e68e2..7230dcdf7 100644 --- a/docs/proposals/Tag-Digest-CoExist.md +++ b/docs/proposals/Tag-Digest-CoExist.md @@ -7,9 +7,6 @@ Current User scenarios: 1. This results in the engineer’s time being wasted on manually mapping between the image digest and the version from the image repository. 1. All my observability dashboards rely on tags but now I need to manually know which tags belong to which digests and somehow also make my dashboards map between those. This is a big hassle. -Other user scenarios that encompass tag mutations NOT related to the current issue which users might want: -1. In case of the pod spec already having both tag and digest, I want to be able to mutate the tag regardless and update the digest accordingly instead of leaving the digest in place. - ## Solution Overview The current solution has been chosen on the basis that Ratify is only meant to mutate from tags to digest as tags are mutable and digests are the ultimate source of truth. This solution is also prepared with the community recommendation of using digests instead of tags in mind with digests being the ultimate source of truth for artifact verification. @@ -43,20 +40,23 @@ The options can work in conjunction to provide the required mutation output. Here, `Latest` tag’s digest = `xxxx` + `v1.2.4` tag’s digest = `yyyy` | Config | Input | Output | | ------ | ----- | ------ | -| retainMutatedTag: false | docker.io/nginx | docker.io/nginx@sha256:xxxxx | -| | docker.io/nginx:latest | docker.io/nginx@sha256:xxxxx | +| retainMutatedTag: false | docker.io/nginx | docker.io/nginx@sha256:xxxx | +| | docker.io/nginx:latest | docker.io/nginx@sha256:xxxx | | | docker.io/nginx:v1.2.4 | docker.io/nginx@sha256:yyyy | | | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx@sha256:xxxx | | | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx@sha256:yyyy | +| | docker.io/nginx@sha256:xxxx | docker.io/nginx@sha256:xxxx | | retainMutatedTag: true | docker.io/nginx | docker.io/nginx:latest@sha256:xxxx | | | docker.io/nginx:v1.2.4 | docker.io/nginx:v1.2.4@sha256:yyyy | | | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx:latest@sha256:xxxx | | | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx:v1.2.4@sha256:yyyy | +| | docker.io/nginx@sha256:xxxx | docker.io/nginx:latest@sha256:xxxx | ### Implementation @@ -71,8 +71,9 @@ provider: crt: "" ... mutation: - enable: true + // enable: true retainMutatedTag: false // (default) + enableMutation: true // deprecated, enable and use mutation.enabled instead. If both are used, `mutation.enable` will be preferred ``` The `retainMutatedTag` option will be available for anyone wanting to control if they want to completely remove tags (the default) or have both tags + digest in the resulting output. From b6cdf2ef21befba0ebc751ee2eb7fd6dec60d7c8 Mon Sep 17 00:00:00 2001 From: Sushant Adhikari Date: Thu, 12 Sep 2024 11:03:14 +1000 Subject: [PATCH 4/4] Fix based on discussion --- docs/proposals/Tag-Digest-CoExist.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/proposals/Tag-Digest-CoExist.md b/docs/proposals/Tag-Digest-CoExist.md index 7230dcdf7..1b8e6d5d7 100644 --- a/docs/proposals/Tag-Digest-CoExist.md +++ b/docs/proposals/Tag-Digest-CoExist.md @@ -30,11 +30,13 @@ A new config block shall be added in the helm chart’s `values.yaml` which will This feature will be available through the new `provider.mutation` config block which will make the `provider.enableMutation` option obsolete. A new boolean sub-config of `provider.mutation.enable` will be added to facilitate this existing feature. +A new sub config `mutationStyle` will be added to facilitate the type of mutation the user owuld want. The following sub-options will be added to incorporate additional configuration during mutation. -| Option Name | Implemented / Designed in the current solution? | Summary | Incoming Spec Condition | Upstream calls for Subject Descriptor? | Default Option | +| `mutationStyle` | Implemented / Designed in the current solution? | Summary | Incoming Spec Condition | Upstream calls for Subject Descriptor? | Default Option | | ----------- | ----------------------------------------------- | ------- | ----------------------- | -------------------------------------- | -------------- | -| retainMutatedTag | Y | Retain the pre-existing tag during mutation / Do not strip the tag away if both tag & digest pre-exists |Contains Tag, Does not contain Digest / Contains Tag, Contains Digest | Y / N | false | +| `retain-mutated-tag` | Y | Retain the pre-existing tag during mutation / Do not strip the tag away if both tag & digest pre-exists |Contains Tag, Does not contain Digest / Contains Tag, Contains Digest | Y / N | false | +| `digest-only` | Y | Mutate tag to digest, stripping the tag away | Contains Tag, Does not contain Digest / Contains Tag, Contains Digest | Y / N | true | The options can work in conjunction to provide the required mutation output. Here, @@ -46,21 +48,23 @@ Here, | Config | Input | Output | | ------ | ----- | ------ | -| retainMutatedTag: false | docker.io/nginx | docker.io/nginx@sha256:xxxx | +| `mutationStyle: "digest-only"` | docker.io/nginx | docker.io/nginx@sha256:xxxx | | | docker.io/nginx:latest | docker.io/nginx@sha256:xxxx | | | docker.io/nginx:v1.2.4 | docker.io/nginx@sha256:yyyy | | | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx@sha256:xxxx | | | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx@sha256:yyyy | | | docker.io/nginx@sha256:xxxx | docker.io/nginx@sha256:xxxx | -| retainMutatedTag: true | docker.io/nginx | docker.io/nginx:latest@sha256:xxxx | +| `mutationStyle: "retain-mutated-tag"` | docker.io/nginx | docker.io/nginx:latest@sha256:xxxx | | | docker.io/nginx:v1.2.4 | docker.io/nginx:v1.2.4@sha256:yyyy | | | docker.io/nginx:latest@sha256:xxxx | docker.io/nginx:latest@sha256:xxxx | | | docker.io/nginx:v1.2.4@sha256:yyyy | docker.io/nginx:v1.2.4@sha256:yyyy | -| | docker.io/nginx@sha256:xxxx | docker.io/nginx:latest@sha256:xxxx | +| | docker.io/nginx@sha256:xxxx | docker.io/nginx@sha256:xxxx | + +An enum style config has been proposed so it does not overcrowd the `provider.mutation` block. Both, addition of new mutation styles as well as parsing on the code side will be easier with this approach. ### Implementation -The `retainMutatedTag` config will be implemented to retain the tag in the resulting spec image. The default option for this config will be `false` to keep supporting the existing config parameter. +The `mutationStyle` config will be implemented to retain the tag in the resulting spec image. The default option for this config will be `digest-only` to keep supporting the existing config parameter. Options of provider.mutation.enable and provider.mutation.retainMutatedTag shall be added into Helm. Example: @@ -72,11 +76,11 @@ provider: ... mutation: // enable: true - retainMutatedTag: false // (default) + mutationStyle: "digest-only" // (default), other options are "retain-mutated-tag" enableMutation: true // deprecated, enable and use mutation.enabled instead. If both are used, `mutation.enable` will be preferred ``` -The `retainMutatedTag` option will be available for anyone wanting to control if they want to completely remove tags (the default) or have both tags + digest in the resulting output. +The `retain-mutated-tag` option will be available for anyone wanting to control if they want to completely remove tags (the default) or have both tags + digest in the resulting output. ## Performance Impact The solution should have very little performance impact considering addition of code will not have any network connectivity related feature. Addition of code mostly should adhere to if-else clauses and other small regex additions.