Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Create proposal for annotation-based filtering #1797

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions docs/proposals/Annotation-Based-Artifact-Filtration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Annotation Based Artifact Filteration

## Problem/Motivation

When configuring a verifier in Ratify, we set the artifact type the verifier should work on. In such case, Ratify will verify all referrers of a given subject that have a matching artifact type using the verifier.
In some cases, this could lead to a wrong behavior. For instance, Vulnerability Artifacts are outdated once a new artifact is written to the repository, as such there is no use for verifying both the new one and the old one. On the other hand, in performing signature verification using Notary, we may wish to attest more than one signature, because there may be multiple signatures attached to a given artifact, but our "tenant" only trusts a subset of them.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In some cases, this could lead to a wrong behavior. For instance, Vulnerability Artifacts are outdated once a new artifact is written to the repository, as such there is no use for verifying both the new one and the old one. On the other hand, in performing signature verification using Notary, we may wish to attest more than one signature, because there may be multiple signatures attached to a given artifact, but our "tenant" only trusts a subset of them.
In some cases, this could lead to a wrong behavior. For instance, Vulnerability Artifacts are outdated once a new artifact is written to the repository, as such there is no use for verifying both the new one and the old one. On the other hand, in performing Notary Project signature verification, we may wish to attest more than one signature, because there may be multiple signatures attached to a given artifact, but our "tenant" only trusts a subset of them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current behavior of Notary Project signature verification is:

  • if 1 out of N signatures passes verification, then the overall signature verification succeeds.
  • If all signatures failed validation, then the overall signature verification fails

Copy link
Collaborator

@yizha1 yizha1 Sep 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@binbin-li If I remembered correctly, verifying multiple signatures can be achieved through Rego policies?


The issue with verifying all the matching artifacts could also lead to performance issues, each "verification" process hides within a request to pull the artifcat manifest, and the blobs containing the actual data.
In previous studies made by the ratify team, it was observed that opverloading the registry with requests could lead to errors and throtteling. (see: https://ratify.dev/docs/reference/performance)

Given the performance study listed above, in order to provide the best experience for Ratify's users ratify would reduce the load it generates on an the registry, thus reducing the chance for throtteling.

# Proposed Solution

Ratify uses the Referrer API (see: https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers) in order to obtain the list of attached artifacts of a given subject artifact. The response body for this request is a generated OCI image index, that looks like this:

```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 1234,
"digest": "sha256:a1a1a1...",
"artifactType": "application/vnd.example.sbom.v1",
"annotations": {
"org.opencontainers.image.created": "2022-01-01T14:42:55Z",
"org.example.sbom.format": "json"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 1234,
"digest": "sha256:a2a2a2...",
"artifactType": "application/vnd.example.signature.v1",
"annotations": {
"org.opencontainers.image.created": "2022-01-01T07:21:33Z",
"org.example.signature.fingerprint": "abcd"
}
},
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"size": 1234,
"digest": "sha256:a3a3a3...",
"annotations": {
"org.opencontainers.image.created": "2023-01-01T07:21:33Z",
}
}
]
}
```

The image index response is requried by API to include the image annotations, which gives Ratify to oprrotunity to perform some basic filtration before invoking the verifier on the listed artifacts. The exact mechanism for requiring the filtration should be specific to each verifier as the behavior and logic differs based on is actually being attested, as such, it will be part of the verifier configuration.

# Filtration Methods

## Age Based Filtration
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still annotation-based filtration, and it just uses the annotation org.opencontainers.image.created to achieve the goal. So I think there is only one filtration method, but currently there are two scenarios, one is for filtering out latest artifact, another is for filtering out a subset of artifacts.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but the verification process of both is different.
Age based verification requires context, you need to be aware of all the referrers, while annotation based in specific to the artifact that is currently being verified.

While age based has to be implemented in the executor, the annotation filtering can be done even in the skel method of the verifier, it could return a new type of response to show that verification was skipped and the artifact won't be included in the final verification response.

Perhaps we should not bundle both features in the same proposal ?


Assuming artifacts are generated by ORAS, they all have a `org.opencontainers.image.created` annotation, that markes the creation date of the artifact. Based on it, Ratify could filter out stale artifacts and only evaluate the latest image. To achieve this, Ratify would have to read all the referrers, ordering them based on the artifact age, and only pass the latest one to the corresponding verifier.

This kind of filtering strategy is best used on artifacts that are rapidly changing, for example Vulnerability Assessment artifacts that are immedietly oudated once a new artifact is pushed to the registry.

## Annotation Value based filteration

Ratify could perform string comparison checks in order to evaluate if a given arifact should be evaluted by a given verifier or not, the flitering would be made by checking if a given annotation
is part of a list of approved annotation values.

To illustrate an example usage of this kind of filtration we could take a look at the following notary artifact manifest:

```json
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 7023,
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b864",
"signatures": [
{
"mediaType": "applicaiton/vnd.cncf.notary.x509-signature.config.v2",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this media type applicaiton/vnd.cncf.notary.x509-signature.config.v2 correct?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied it from their website, perhaps it is not up to date, i'll check again and update the doc.

"digest": "sha256:134c7fe821b9d359490cd009ce7ca322453f4f2d0",
"size": 1337,
"annotations": {
"org.opencontainers.image.signature.fingerprint": "43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8"
}
}
]
}
]
}
```

We could define a notation verifier configuration that would require the artifact manifest to contain the annotation `org.opencontainers.image.signature.fingerprint` and that is should have a known fingerprint value. this would enable a scenario where the user configures three notation verifiers, one in each namespace:
* Namepsace A -> Fingerprint should match [X]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate more about the problem that fingerprint filtering is going to be solved? If it is to distinguish different signers, we can configure Notation Verifier with different trusted identities for different namespaces. For example, images pulled in namespace A are signed by Signer-A, then we can configure the trusted identities that Signer-A used for namespace A.

Or is your scenario to distinguish signatures that are generated by the same Signer (trusted identity)? Then what is the difference in signatures generated by the same Signer? If it is about age, maybe we can use aged-filtering to verify only the latest signature?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Basically, I referred to a scenario in which I as a namespace owner I require that all certificate chains will contains a specific signer, since the signature chain is part of the artifact annotations, I can verify it before even going through all whole process of verifying the actual signature.

* Namespace B -> Fingerprint should match [Y]
* Namespace C -> Fingerprint should match either [X, Y]

## User experiences

This section describes the experience that users interact with Ratify using the proposed solution. In summary, the propsed solution suggest we should allow for filtering of artifact based on annotations, and as such the following section describes how the customer would configure the filtration.

Seeing that filteration is unique to each verifier, it should be configured in the verifier itself, as such, in order to maintain backwards compatability, it is important to note that if no filteration is configured the default behavior would be to evaluate all artifacts.

### Defining artifact age based filtering

To support artifact age based filtering, we would add an additional field to the verifier configuration:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will the filtering capability be available for both k8s and cli scenario.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it should be available on both methods, the examples for K8s are perhaps just out of lazyness :)
I'll update the proposal to include examples of CLI configurations.


```yaml
apiVersion: config.ratify.deislabs.io/v1beta1
kind: Verifier # NamespacedVerifier has the same spec.
metadata:
name: test-verifier
spec:
name: # REQUIRED: [string], the unique type of the verifier (notation, cosign)
artifactType: # REQUIRED: [string], comma seperated list, artifact type this verifier handles
artifactAgeFilter: # Optional: [string], one of the following 'latest' to verify only the last artifact, or all to verify all artifacts. defaults to all.
Copy link
Collaborator

@susanshi susanshi Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if artifactAgeFilter may contain different values based on verifier, should this be moved to "parameters"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added it here because each verifier/namespaced verifier configuration is evaluated as a different "verifier" by ratify.

Since the properties part is parsed by the verifier code itself, and as you said, we wish to filter in the executor, then it would be better to place those fields outside the scope of parameters.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will the value of artifactAgeFilter be a define set of string? an expression or key:value pair

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be as a single string, either "latest" or "all"

artifactAnnotationFilterStrategy: # Optional: [Object], object defining annotation list filtering, when not defined, no filtering is done.
- annotation: # Required: [String], the annotation key
- values: # Required: [[]string], the list of allowed values.
address: # OPTIONAL: [string], Plugin path, defaults to value of env "RATIFY_CONFIG" or "~/.ratify/plugins"
version: # OPTIONAL: [string], Version of the external plugin, defaults to 1.0.0. On ratify initialization, the specified version will be validated against the supported plugin version.
source:
artifact: # OPTIONAL: [string], Source location to download the plugin binary, learn more at docs/reference/dynamic-plugins.md e.g. wabbitnetworks.azurecr.io/test sample-verifier-plugin:v1
parameters: # OPTIONAL: [object] Parameters specific to this verifier
```

# References

* [Ratify Performance at Scale Study](https://ratify.dev/docs/reference/performance)
* [Referrer API in Distribution Spec](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers)