From 6d62a55fc08d5f93666bdd5b2aa9ea465862adab Mon Sep 17 00:00:00 2001 From: Binbin Li Date: Mon, 27 Jan 2025 10:33:45 +0800 Subject: [PATCH] fix: enforce host checking before exchanging a refresh token (#2069) (#2071) Signed-off-by: Binbin Li --- charts/ratify/README.md | 247 +++++++++--------- charts/ratify/templates/store.yaml | 4 + charts/ratify/values.yaml | 1 + .../oras/authprovider/azure/azureidentity.go | 16 +- .../authprovider/azure/azureidentity_test.go | 2 + .../azure/azureworkloadidentity.go | 16 +- .../azure/azureworkloadidentity_test.go | 5 + pkg/common/oras/authprovider/azure/const.go | 1 + pkg/common/oras/authprovider/azure/helper.go | 53 ++++ .../oras/authprovider/azure/helper_test.go | 90 +++++++ 10 files changed, 307 insertions(+), 128 deletions(-) diff --git a/charts/ratify/README.md b/charts/ratify/README.md index 544abe32d..9aff98c69 100644 --- a/charts/ratify/README.md +++ b/charts/ratify/README.md @@ -36,126 +36,127 @@ Values marked `# DEPRECATED` in the `values.yaml` as well as **DEPRECATED** in t ## Parameters -| Parameter | Description | Default | -| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | -| image.repository | Ratify app image | `ghcr.io/ratify-project/ratify` | -| image.crdrepository | Ratify CRD install Image | `ghcr.io/ratify-project/ratify-crds` | -| image.tag | Image tag | `` | -| image.pullPolicy | Image pull policy | `IfNotPresent` | -| nameOverride | Overrides the ratify.name used to determine the ratify full name template | `` | -| fullnameOverride | Overrides the ratify applicaiton full name template | `` | -| replicaCount | The number of Ratify replicas in deployment | 1 | -| affinity | Pod affinity for the Ratify deployment | `{}` | -| tolerations | Pod tolerations for the Ratify deployment | `[]` | -| env | Environment variables for Ratify container | `[]` | -| notationCerts | An array of public certificate/certificate chain used to create inline certstore used by Notation verifier | `` | -| cosignKeys | An array of public keys used to create inline key management providers used by Cosign verifier | `[]` | -| notation.enabled | Enables/disables the built-in notation verifier. MUST be set to true for notation verification. | `true` | -| notation.trustPolicies | An array of trustPolicies. Each [trustPolicy](https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#trust-policy-properties:~:text=Trust%20Policy%20Properties) must define the following three attributes: `registryScopes`, `trustedIdentities`, `trustStores` | [] | -| notation.trustPolicies[0].registryScopes | An array of scopes relevant to the single trust policy configured in notation verifier. | `["*"]` | -| notation.trustPolicies[0].trustedIdentities | An array of trusted identities relevant to the single trust policy configured in notation verifier. | `["*"]` | -| notation.trustPolicies[0].trustStores | An array of trust stores relevant to the single trust policy configured in notation verifier. Each trustStore is defined as [ca\|tsa\|signingAuthority]:[notationCerts[i]\|AzureKeyVault]. Example: ca:notationCerts[0] | `` | -| notation.trustPolicies[0].registryScopes | An array of registryScopes relevant to the single trust policy configured in notation verifier. | `["*"]` | -| cosign.enabled | Enables/disables cosign tag-based signature lookup in ORAS store. MUST be set to true for cosign verification. | `true` | -| cosign.scopes | An array of scopes relevant to the single trust policy configured in Cosign verifier. A scope of '*' is a global wildcard character to represent all images apply. | `["*"]` | -| cosign.rekorURL | URL string reference to remote rekor server. If not specified, implementation will default to use Rekor public good instance `https://rekor.sigstore.dev`. | `` | -| cosign.tLogVerify | Enables/disables verification of presence of signature in Transparency log. | `true` | -| cosign.keyless.ctLogVerify | Enables/disables verification of presence of Secure Certificate Timestamp (SCT) in transparency log | `true` | -| cosign.keyless.certificateIdentity | String certificate identity used for exact identity match during verification. Either `certificateIdentity` or `certificateIdentityRegExp` MUST be defined, but both cannot be defined at together | `` | -| cosign.keyless.certificateIdentityRegExp | String certificate identity regular expression for identity matching during verification. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either `certificateIdentity` or `certificateIdentityRegExp` MUST be defined, but both cannot be defined together | `` | -| cosign.keyless.certificateOIDCIssuer | String certificate OIDC issuer for exact issuer matching during verification. Either `certificateOIDCIssuer` or `certificateOIDCIssuerRegExp` MUST be defined, but both cannot be defined together | `` | -| cosign.keyless.certificateOIDCIssuerRegExp | String certificate OIDC issuer regular expression for issuer matching during verification. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either `certificateOIDCIssuer` or `certificateOIDCIssuerRegExp` MUST be defined, but both cannot be defined together | `` | -| vulnerabilityreport.enabled | Enables/disables installation of vulnerability report verifier | `false` | -| vulnerabilityreport.passthrough | Enables/disables passthrough. All validation except `maximumAge` are disregarded and report content is added to verifier report | `false` | -| vulnerabilityreport.schemaURL | URL for JSON schema to validate report against | `` | -| vulnerabilityreport.createdAnnotationName | Overrides the default created annotation (`org.opencontainers.image.created`) to search for | `` | -| vulnerabilityreport.maximumAge | Maximum age report can be based on timestamp in stored at creation annotation. Formatted based on [time.Duration](https://pkg.go.dev/time#ParseDuration). A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms" or "24h". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". | `` | -| vulnerabilityreport.notaryProjectSignatureRequired | Enables/disable notary project signature verification attached to vulnerability report. Refer to notation verifier [documentation](https://ratify.dev/docs/reference/crds/verifiers#notation) to install + configure keys. | `false` | -| vulnerabilityreport.disallowedSeverities | List of severities to disallow (strings). Common severities: `low`, `medium`, `high`, `critical`, `unknown` | `[]` | -| vulnerabilityreport.denylistCVEs | List of CVE IDs that cannot exist in the vulnerability report | `[]` | -| sbom.enabled | Enables/disables installation of sbom verification configuration | `false` | -| sbom.notaryProjectSignatureRequired | requires validation of sbom notation signature | `false` | -| sbom.disallowedLicenses | list of disallowed licenses | [] | -| sbom.disallowedPackages | list of disallowed packages defined by package name and version. For example: --set sbom.disallowedPackages[0].name="busybox" --set sbom.disallowedPackages[0].version="1.36.1-r0" | [] | -| resources.limits.cpu | CPU limits of Ratify Deployment | `1000m` | -| resources.limits.memory | Memory limits of Ratify Deployment | `512Mi` | -| resources.requests.cpu | CPU request of Ratify Deployment | `600m` | -| resources.requests.memory | Memory request of Ratify Deployment | `512Mi` | -| serviceAccount.create | Create new dedicated Ratify service account | `true` | -| serviceAccount.name | Name of Ratify service account to create | `ratify-admin` | -| serviceAccount.annotations | Annotations to add to the service account | `{}` | -| gatekeeper.version | Determines the Gatekeeper CRD versioning | `3.18.0` | -| gatekeeper.namespace | Namespace Gatekeeper is installed | `gatekeeper-system` | -| instrumentation.metricsEnabled | Initializes the configured metrics provider | `true` | -| instrumentation.metricsType | Specifies the metrics provider type | `prometheus` | -| instrumentation.metricsPort | The metrics server port on Ratify container | `8888` | -| oras.useHttp | Disables TLS verification and uses `http` for registry communication (Note: use for development purposes ONLY) | `false` | -| oras.authProviders.azureWorkloadIdentityEnabled | Enables Azure Workload Identity authentication provider | `false` | -| oras.authProviders.azureManagedIdentityEnabled | Enables Azure Managed Identity authentication provider | `false` | -| oras.authProviders.k8secretsEnabled | Enables kubernetes secrets authentication provider for registry interactions | `false` | -| oras.authProviders.awsEcrBasicEnabled | Enables AWS ECR basic authentication provider | `false` | -| oras.authProviders.awsApiOverride.enabled | Enables API URL overrides | `false` | -| oras.authProviders.awsApiOverride.endpoint | Overrides ECR endpoint | `` | -| oras.authProviders.awsApiOverride.partition | Overrides ECR partition in the endpoint URL | `aws` | -| oras.authProviders.awsApiOverride.region | Overrides ECR region in the endpoint URL | `` | -| oras.authProviders.alibabacloudAcrBasicEnabled | Enables Alibaba Cloud ACR basic authentication provider | `false` | -| oras.cache.enabled | Enables ORAS store cache for ListReferrers and GetSubjectDescriptor. TTL-based cache may cause inconsistency between cache and data source. Please disable it if strong consistency is required.operations | `true` | -| oras.cache.ttl | Sets the ttl for ORAS store in seconds. cache | `10` | -| provider.tls.crt | Ratify server's tls public certificate | `` | -| provider.tls.key | Ratify server's tls private key | `` | -| provider.tls.caCert | Ratify server's CA public certificate. TLS certificate is generated using CA. | `` | -| provider.tls.caKey | Ratify server's CA private key. | `` | -| provider.tls.cabundle | Base64 encoded CA bundle used for the 'caBundle' property of the Provider CR of Gatekeeper. CRD | `` | -| provider.timeout.validationTimeoutSeconds | Verify request handler timeout in seconds. This MUST match the configured Gatekeeper `validatingWebhookTimeoutSeconds`. | `5` | -| provider.timeout.mutationTimeoutSeconds | Mutate request handler timeout in seconds. This MUST match the configured Gatekeeper `mutatingWebhookTimeoutSeconds` | `2` | -| provider.cache.enabled | Enables/disables non-ORAS store caches such as request cache and authentication cache. | `true` | -| provider.cache.type | The cache provider for global cache. (use `dapr` for HA scenarios) | `ristretto` | -| provider.cacheSizeMb | Local cache max size allocated (applicable only if `ristretto` cache type selected) | `256` | -| provider.ttl | TTL in seconds of global cache | `10s` | -| provider.name | The state store provider name used with dapr (applicable only if `dapr` cache type selected) | `dapr-redis` | -| provider.enableMutation | Enables/disables tag-to-digest mutation for all admission resource creations. It is highly recommended to enable mutation since the verified digest may be different from the one run. | `true` | -| podAnnotations | Adds specified annotations to Ratify deployment | `{}` | -| podLabels | Adds specified labels to Ratify deployment | `{}` | -| enableRuntimeDefaultSeccompProfile | Sets the container's `seccomp` profile to be RuntimeDefault | `true` | -| healthPort | Liveness & readiness probe's port for ratify container | `9099` | -| rbac.create | Enable/disable RBAC roles for ratify manager | `true` | -| upgradeCRDs.enabled | Enable/disable Ratify CRD upgrades as pre-install chart hooks | `true` | -| upgradeCRDs.extraRules | List of rules to add to Ratify CRD upgrade ClusterRole | `[]` | -| crds.affinity | Pod affinity for the upgrade CRD Job | `{}` | -| crds.tolerations | Pod tolerations for the upgrade CRD Job | `[]` | -| crds.nodeSelector | | `{kubernetes.io/os: linux}` | -| crds.resources | Resource limits/requests for ratify upgrade CRD job | `` | -| crds.securityContext.allowPrivilegeEscalation | Enables/disables privilege elevation for crd upgrade container | `false` | -| crds.securityContext.capabilities.drop | List of allowed capabilities for crd upgrade container | `['ALL']` | -| crds.securityContext.readOnlyRootFilesystem | Enable/disable read-only filesystem for crd upgrade container | `true` | -| crds.securityContext.runAsGroup | Sets user group context | `65532` | -| crds.securityContext.runAsNonRoot | Enable/disable root user role | `true` | -| crds.securityContext.runAsUser | Sets user context | `65532` | -| policy.useRego | Enables/disable OPA rego policy CRD | `false` | -| logger.formatter | Type of log formatter. Can be set to `text`, `json` or `logstash` output | `text` | -| logger.level | Sets the log level | `info` | -| logger.requestHeaders.traceIDHeaderName | List of headers that include the trace ID in the external data requests to Ratify. The same headers will be passed to upstream services like remote registries. e.g. Set it to `x-ms-correlation-request-id` to trace across Azure. | `[]` | -| crl.cache.enabled | Enables/disables CRL Cache | `true` | -| featureFlags.RATIFY_CERT_ROTATION | Enables/disables tls certificate rotation | `false` | -| featureFlags.RATIFY_EXPERIMENTAL_HIGH_AVAILABILITY | **EXPERIMENTAL** Enables/disables high availability mode including distributed caching. | `false` | -| azureWorkloadIdentity.clientId | ClientID of AAD application/Managed identity associated with Workload Identity | `` | -| azureManagedIdentity.clientId | ClientID of Managed identity | `` | -| azureManagedIdentity.tenantId | TenantID of Managed Identity resource | `` | -| azurekeyvault.enabled | Enables/disables Azure Key Vault key management provider. If you are using a custom chart, certificate store should be referenced through a Verifier CR. | `false` | -| azurekeyvault.vaultURI | Vault URI for Azure Key Vault | `` | -| azurekeyvault.tenantId | Tenant ID of the configured Azure Key Vault resource | `` | -| azurekeyvault.certificates | An array of certificate objects identified by `name` and `version` (optional) stored in Azure Key Vault | `[]` | -| azurekeyvault.keys | An array of key objects identified by `name` and `version` (optional) stored in Azure Key Vault | `[]` | -| azurekeyvault.refreshInterval | time duration to refresh the certificates/keys. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Example: 1h, 30m, 1h30m. If it's not set, the refresh functionality will be disabled. | `` | -| alibabacloudAcrConfig.defaultInstanceId | Default instance ID of the Alibaba Cloud Registry where the target artifacts stored | `` | -| alibabacloudAcrConfig.acrInstancesConfig | When images need to be pulled from multiple instances of Aliababa Cloud Registry, the instanceName and instanceId of the instances need to be defined separately in the list, e.g. acrInstancesConfig:
- instanceName: name1
instanceId: cri-xxx1
- instanceName: name2
instanceId: cri-xxx2
| `[]` | -| notationCert | **DEPRECATED** Please switch to `notationCerts` to specify an array of verification certificates. Public certificate/certificate chain used to create inline certstore used by Notation verifier. | `` | -| akvCertConfig.enabled | **DEPRECATED** Please use `azurekeyvault.enabled` instead. Enables/disables Azure Key Vault certificate store. If you are using a custom chart, certificate store should be referenced through a Verifier CR. References in ConfigMap will not be correctly resolved. | `false` | -| akvCertConfig.vaultURI | **DEPRECATED** Please use `azurekeyvault.vaultURI` instead. Vault URI for AKV configured | `` | -| akvCertConfig.cert1Name | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact name of the certificate stored in AKV. | `` | -| akvCertConfig.cert1Version | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact version of certificate to use from AKV.certificates | `` | -| akvCertConfig.cert2Name | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact name of the certificate stored in AKV. | `` | -| akvCertConfig.cert2Version | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact version of certificate to use from AKV. | `` | -| akvCertConfig.certificates | **DEPRECATED** Please use `azurekeyvault.certificates` instead. An array of certificate objects identified by `name` and `version` stored in AKV | `` | -| akvCertConfig.tenantId | **DEPRECATED** Please use `azurekeyvault.certificates` instead. TenantID of the configured AKV resource | `` | -| cosign.key | **DEPRECATED** Please use `cosignKeys` instead. Public key used by cosign verifier | `` | +| Parameter | Description | Default | +| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| image.repository | Ratify app image | `ghcr.io/ratify-project/ratify` | +| image.crdrepository | Ratify CRD install Image | `ghcr.io/ratify-project/ratify-crds` | +| image.tag | Image tag | `` | +| image.pullPolicy | Image pull policy | `IfNotPresent` | +| nameOverride | Overrides the ratify.name used to determine the ratify full name template | `` | +| fullnameOverride | Overrides the ratify applicaiton full name template | `` | +| replicaCount | The number of Ratify replicas in deployment | 1 | +| affinity | Pod affinity for the Ratify deployment | `{}` | +| tolerations | Pod tolerations for the Ratify deployment | `[]` | +| env | Environment variables for Ratify container | `[]` | +| notationCerts | An array of public certificate/certificate chain used to create inline certstore used by Notation verifier | `` | +| cosignKeys | An array of public keys used to create inline key management providers used by Cosign verifier | `[]` | +| notation.enabled | Enables/disables the built-in notation verifier. MUST be set to true for notation verification. | `true` | +| notation.trustPolicies | An array of trustPolicies. Each [trustPolicy](https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md#trust-policy-properties:~:text=Trust%20Policy%20Properties) must define the following three attributes: `registryScopes`, `trustedIdentities`, `trustStores` | [] | +| notation.trustPolicies[0].registryScopes | An array of scopes relevant to the single trust policy configured in notation verifier. | `["*"]` | +| notation.trustPolicies[0].trustedIdentities | An array of trusted identities relevant to the single trust policy configured in notation verifier. | `["*"]` | +| notation.trustPolicies[0].trustStores | An array of trust stores relevant to the single trust policy configured in notation verifier. Each trustStore is defined as [ca\|tsa\|signingAuthority]:[notationCerts[i]\|AzureKeyVault]. Example: ca:notationCerts[0] | `` | +| notation.trustPolicies[0].registryScopes | An array of registryScopes relevant to the single trust policy configured in notation verifier. | `["*"]` | +| cosign.enabled | Enables/disables cosign tag-based signature lookup in ORAS store. MUST be set to true for cosign verification. | `true` | +| cosign.scopes | An array of scopes relevant to the single trust policy configured in Cosign verifier. A scope of '*' is a global wildcard character to represent all images apply. | `["*"]` | +| cosign.rekorURL | URL string reference to remote rekor server. If not specified, implementation will default to use Rekor public good instance `https://rekor.sigstore.dev`. | `` | +| cosign.tLogVerify | Enables/disables verification of presence of signature in Transparency log. | `true` | +| cosign.keyless.ctLogVerify | Enables/disables verification of presence of Secure Certificate Timestamp (SCT) in transparency log | `true` | +| cosign.keyless.certificateIdentity | String certificate identity used for exact identity match during verification. Either `certificateIdentity` or `certificateIdentityRegExp` MUST be defined, but both cannot be defined at together | `` | +| cosign.keyless.certificateIdentityRegExp | String certificate identity regular expression for identity matching during verification. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either `certificateIdentity` or `certificateIdentityRegExp` MUST be defined, but both cannot be defined together | `` | +| cosign.keyless.certificateOIDCIssuer | String certificate OIDC issuer for exact issuer matching during verification. Either `certificateOIDCIssuer` or `certificateOIDCIssuerRegExp` MUST be defined, but both cannot be defined together | `` | +| cosign.keyless.certificateOIDCIssuerRegExp | String certificate OIDC issuer regular expression for issuer matching during verification. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either `certificateOIDCIssuer` or `certificateOIDCIssuerRegExp` MUST be defined, but both cannot be defined together | `` | +| vulnerabilityreport.enabled | Enables/disables installation of vulnerability report verifier | `false` | +| vulnerabilityreport.passthrough | Enables/disables passthrough. All validation except `maximumAge` are disregarded and report content is added to verifier report | `false` | +| vulnerabilityreport.schemaURL | URL for JSON schema to validate report against | `` | +| vulnerabilityreport.createdAnnotationName | Overrides the default created annotation (`org.opencontainers.image.created`) to search for | `` | +| vulnerabilityreport.maximumAge | Maximum age report can be based on timestamp in stored at creation annotation. Formatted based on [time.Duration](https://pkg.go.dev/time#ParseDuration). A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms" or "24h". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". | `` | +| vulnerabilityreport.notaryProjectSignatureRequired | Enables/disable notary project signature verification attached to vulnerability report. Refer to notation verifier [documentation](https://ratify.dev/docs/reference/crds/verifiers#notation) to install + configure keys. | `false` | +| vulnerabilityreport.disallowedSeverities | List of severities to disallow (strings). Common severities: `low`, `medium`, `high`, `critical`, `unknown` | `[]` | +| vulnerabilityreport.denylistCVEs | List of CVE IDs that cannot exist in the vulnerability report | `[]` | +| sbom.enabled | Enables/disables installation of sbom verification configuration | `false` | +| sbom.notaryProjectSignatureRequired | requires validation of sbom notation signature | `false` | +| sbom.disallowedLicenses | list of disallowed licenses | [] | +| sbom.disallowedPackages | list of disallowed packages defined by package name and version. For example: --set sbom.disallowedPackages[0].name="busybox" --set sbom.disallowedPackages[0].version="1.36.1-r0" | [] | +| resources.limits.cpu | CPU limits of Ratify Deployment | `1000m` | +| resources.limits.memory | Memory limits of Ratify Deployment | `512Mi` | +| resources.requests.cpu | CPU request of Ratify Deployment | `600m` | +| resources.requests.memory | Memory request of Ratify Deployment | `512Mi` | +| serviceAccount.create | Create new dedicated Ratify service account | `true` | +| serviceAccount.name | Name of Ratify service account to create | `ratify-admin` | +| serviceAccount.annotations | Annotations to add to the service account | `{}` | +| gatekeeper.version | Determines the Gatekeeper CRD versioning | `3.18.0` | +| gatekeeper.namespace | Namespace Gatekeeper is installed | `gatekeeper-system` | +| instrumentation.metricsEnabled | Initializes the configured metrics provider | `true` | +| instrumentation.metricsType | Specifies the metrics provider type | `prometheus` | +| instrumentation.metricsPort | The metrics server port on Ratify container | `8888` | +| oras.useHttp | Disables TLS verification and uses `http` for registry communication (Note: use for development purposes ONLY) | `false` | +| oras.authProviders.azureWorkloadIdentityEnabled | Enables Azure Workload Identity authentication provider | `false` | +| oras.authProviders.azureManagedIdentityEnabled | Enables Azure Managed Identity authentication provider | `false` | +| oras.authProviders.azureContainerRegistryEndpoints | List of Azure Container Registry endpoints that the configured store can access. Endpoint is either a fully qualified domain name or a wildcard domain name following [RFC 1034](https://www.ietf.org/rfc/rfc1034.txt) | `[]` | +| oras.authProviders.k8secretsEnabled | Enables kubernetes secrets authentication provider for registry interactions | `false` | +| oras.authProviders.awsEcrBasicEnabled | Enables AWS ECR basic authentication provider | `false` | +| oras.authProviders.awsApiOverride.enabled | Enables API URL overrides | `false` | +| oras.authProviders.awsApiOverride.endpoint | Overrides ECR endpoint | `` | +| oras.authProviders.awsApiOverride.partition | Overrides ECR partition in the endpoint URL | `aws` | +| oras.authProviders.awsApiOverride.region | Overrides ECR region in the endpoint URL | `` | +| oras.authProviders.alibabacloudAcrBasicEnabled | Enables Alibaba Cloud ACR basic authentication provider | `false` | +| oras.cache.enabled | Enables ORAS store cache for ListReferrers and GetSubjectDescriptor. TTL-based cache may cause inconsistency between cache and data source. Please disable it if strong consistency is required.operations | `true` | +| oras.cache.ttl | Sets the ttl for ORAS store in seconds. cache | `10` | +| provider.tls.crt | Ratify server's tls public certificate | `` | +| provider.tls.key | Ratify server's tls private key | `` | +| provider.tls.caCert | Ratify server's CA public certificate. TLS certificate is generated using CA. | `` | +| provider.tls.caKey | Ratify server's CA private key. | `` | +| provider.tls.cabundle | Base64 encoded CA bundle used for the 'caBundle' property of the Provider CR of Gatekeeper. CRD | `` | +| provider.timeout.validationTimeoutSeconds | Verify request handler timeout in seconds. This MUST match the configured Gatekeeper `validatingWebhookTimeoutSeconds`. | `5` | +| provider.timeout.mutationTimeoutSeconds | Mutate request handler timeout in seconds. This MUST match the configured Gatekeeper `mutatingWebhookTimeoutSeconds` | `2` | +| provider.cache.enabled | Enables/disables non-ORAS store caches such as request cache and authentication cache. | `true` | +| provider.cache.type | The cache provider for global cache. (use `dapr` for HA scenarios) | `ristretto` | +| provider.cacheSizeMb | Local cache max size allocated (applicable only if `ristretto` cache type selected) | `256` | +| provider.ttl | TTL in seconds of global cache | `10s` | +| provider.name | The state store provider name used with dapr (applicable only if `dapr` cache type selected) | `dapr-redis` | +| provider.enableMutation | Enables/disables tag-to-digest mutation for all admission resource creations. It is highly recommended to enable mutation since the verified digest may be different from the one run. | `true` | +| podAnnotations | Adds specified annotations to Ratify deployment | `{}` | +| podLabels | Adds specified labels to Ratify deployment | `{}` | +| enableRuntimeDefaultSeccompProfile | Sets the container's `seccomp` profile to be RuntimeDefault | `true` | +| healthPort | Liveness & readiness probe's port for ratify container | `9099` | +| rbac.create | Enable/disable RBAC roles for ratify manager | `true` | +| upgradeCRDs.enabled | Enable/disable Ratify CRD upgrades as pre-install chart hooks | `true` | +| upgradeCRDs.extraRules | List of rules to add to Ratify CRD upgrade ClusterRole | `[]` | +| crds.affinity | Pod affinity for the upgrade CRD Job | `{}` | +| crds.tolerations | Pod tolerations for the upgrade CRD Job | `[]` | +| crds.nodeSelector | | `{kubernetes.io/os: linux}` | +| crds.resources | Resource limits/requests for ratify upgrade CRD job | `` | +| crds.securityContext.allowPrivilegeEscalation | Enables/disables privilege elevation for crd upgrade container | `false` | +| crds.securityContext.capabilities.drop | List of allowed capabilities for crd upgrade container | `['ALL']` | +| crds.securityContext.readOnlyRootFilesystem | Enable/disable read-only filesystem for crd upgrade container | `true` | +| crds.securityContext.runAsGroup | Sets user group context | `65532` | +| crds.securityContext.runAsNonRoot | Enable/disable root user role | `true` | +| crds.securityContext.runAsUser | Sets user context | `65532` | +| policy.useRego | Enables/disable OPA rego policy CRD | `false` | +| logger.formatter | Type of log formatter. Can be set to `text`, `json` or `logstash` output | `text` | +| logger.level | Sets the log level | `info` | +| logger.requestHeaders.traceIDHeaderName | List of headers that include the trace ID in the external data requests to Ratify. The same headers will be passed to upstream services like remote registries. e.g. Set it to `x-ms-correlation-request-id` to trace across Azure. | `[]` | +| crl.cache.enabled | Enables/disables CRL Cache | `true` | +| featureFlags.RATIFY_CERT_ROTATION | Enables/disables tls certificate rotation | `false` | +| featureFlags.RATIFY_EXPERIMENTAL_HIGH_AVAILABILITY | **EXPERIMENTAL** Enables/disables high availability mode including distributed caching. | `false` | +| azureWorkloadIdentity.clientId | ClientID of AAD application/Managed identity associated with Workload Identity | `` | +| azureManagedIdentity.clientId | ClientID of Managed identity | `` | +| azureManagedIdentity.tenantId | TenantID of Managed Identity resource | `` | +| azurekeyvault.enabled | Enables/disables Azure Key Vault key management provider. If you are using a custom chart, certificate store should be referenced through a Verifier CR. | `false` | +| azurekeyvault.vaultURI | Vault URI for Azure Key Vault | `` | +| azurekeyvault.tenantId | Tenant ID of the configured Azure Key Vault resource | `` | +| azurekeyvault.certificates | An array of certificate objects identified by `name` and `version` (optional) stored in Azure Key Vault | `[]` | +| azurekeyvault.keys | An array of key objects identified by `name` and `version` (optional) stored in Azure Key Vault | `[]` | +| azurekeyvault.refreshInterval | time duration to refresh the certificates/keys. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Example: 1h, 30m, 1h30m. If it's not set, the refresh functionality will be disabled. | `` | +| alibabacloudAcrConfig.defaultInstanceId | Default instance ID of the Alibaba Cloud Registry where the target artifacts stored | `` | +| alibabacloudAcrConfig.acrInstancesConfig | When images need to be pulled from multiple instances of Aliababa Cloud Registry, the instanceName and instanceId of the instances need to be defined separately in the list, e.g. acrInstancesConfig:
- instanceName: name1
instanceId: cri-xxx1
- instanceName: name2
instanceId: cri-xxx2
| `[]` | +| notationCert | **DEPRECATED** Please switch to `notationCerts` to specify an array of verification certificates. Public certificate/certificate chain used to create inline certstore used by Notation verifier. | `` | +| akvCertConfig.enabled | **DEPRECATED** Please use `azurekeyvault.enabled` instead. Enables/disables Azure Key Vault certificate store. If you are using a custom chart, certificate store should be referenced through a Verifier CR. References in ConfigMap will not be correctly resolved. | `false` | +| akvCertConfig.vaultURI | **DEPRECATED** Please use `azurekeyvault.vaultURI` instead. Vault URI for AKV configured | `` | +| akvCertConfig.cert1Name | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact name of the certificate stored in AKV. | `` | +| akvCertConfig.cert1Version | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact version of certificate to use from AKV.certificates | `` | +| akvCertConfig.cert2Name | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact name of the certificate stored in AKV. | `` | +| akvCertConfig.cert2Version | **DEPRECATED** Please use `azurekeyvault.certificates` instead. Exact version of certificate to use from AKV. | `` | +| akvCertConfig.certificates | **DEPRECATED** Please use `azurekeyvault.certificates` instead. An array of certificate objects identified by `name` and `version` stored in AKV | `` | +| akvCertConfig.tenantId | **DEPRECATED** Please use `azurekeyvault.certificates` instead. TenantID of the configured AKV resource | `` | +| cosign.key | **DEPRECATED** Please use `cosignKeys` instead. Public key used by cosign verifier | `` | diff --git a/charts/ratify/templates/store.yaml b/charts/ratify/templates/store.yaml index 07ec36a9b..6cece47f3 100644 --- a/charts/ratify/templates/store.yaml +++ b/charts/ratify/templates/store.yaml @@ -19,11 +19,15 @@ spec: authProvider: name: azureWorkloadIdentity clientID: {{ .Values.azureWorkloadIdentity.clientId }} + endpoints: + {{- toYaml .Values.oras.authProviders.azureContainerRegistryEndpoints | nindent 8 }} {{- end }} {{- if .Values.oras.authProviders.azureManagedIdentityEnabled }} authProvider: name: azureManagedIdentity clientID: {{ .Values.azureManagedIdentity.clientId }} + endpoints: + {{- toYaml .Values.oras.authProviders.azureContainerRegistryEndpoints | nindent 8 }} {{- end }} {{- if .Values.oras.authProviders.k8secretsEnabled }} authProvider: diff --git a/charts/ratify/values.yaml b/charts/ratify/values.yaml index adfc70078..f7e6a66f2 100644 --- a/charts/ratify/values.yaml +++ b/charts/ratify/values.yaml @@ -98,6 +98,7 @@ oras: authProviders: azureWorkloadIdentityEnabled: false azureManagedIdentityEnabled: false + azureContainerRegistryEndpoints: [] k8secretsEnabled: false awsEcrBasicEnabled: false awsApiOverride: diff --git a/pkg/common/oras/authprovider/azure/azureidentity.go b/pkg/common/oras/authprovider/azure/azureidentity.go index d0369c4dc..c85edf992 100644 --- a/pkg/common/oras/authprovider/azure/azureidentity.go +++ b/pkg/common/oras/authprovider/azure/azureidentity.go @@ -67,11 +67,13 @@ type MIAuthProvider struct { authClientFactory AuthClientFactory registryHostGetter RegistryHostGetter getManagedIdentityToken ManagedIdentityTokenGetter + endpoints []string } type azureManagedIdentityAuthProviderConf struct { - Name string `json:"name"` - ClientID string `json:"clientID"` + Name string `json:"name"` + ClientID string `json:"clientID"` + Endpoints []string `json:"endpoints,omitempty"` } const ( @@ -106,9 +108,12 @@ func (s *azureManagedIdentityProviderFactory) Create(authProviderConfig provider return nil, re.ErrorCodeEnvNotSet.WithDetail("AZURE_CLIENT_ID environment variable is empty").WithComponentType(re.AuthProvider) } } + + endpoints, err := parseEndpoints(conf.Endpoints) if err != nil { - return nil, err + return nil, re.ErrorCodeConfigInvalid.WithError(err) } + // retrieve an AAD Access token token, err := getManagedIdentityToken(context.Background(), client, azidentity.NewManagedIdentityCredential) if err != nil { @@ -121,6 +126,7 @@ func (s *azureManagedIdentityProviderFactory) Create(authProviderConfig provider tenantID: tenant, authClientFactory: &defaultAuthClientFactoryImpl{}, // Concrete implementation getManagedIdentityToken: &defaultManagedIdentityTokenGetterImpl{}, // Concrete implementation + endpoints: endpoints, }, nil } @@ -155,6 +161,10 @@ func (d *MIAuthProvider) Provide(ctx context.Context, artifact string) (provider return provider.AuthConfig{}, re.ErrorCodeHostNameInvalid.WithComponentType(re.AuthProvider) } + if err := validateHost(artifactHostName, d.endpoints); err != nil { + return provider.AuthConfig{}, re.ErrorCodeHostNameInvalid.WithError(err) + } + // need to refresh AAD token if it's expired if time.Now().Add(time.Minute * 5).After(d.identityToken.ExpiresOn) { newToken, err := d.getManagedIdentityToken.GetManagedIdentityToken(ctx, d.clientID) diff --git a/pkg/common/oras/authprovider/azure/azureidentity_test.go b/pkg/common/oras/authprovider/azure/azureidentity_test.go index 8d466d3d1..e42ec56eb 100644 --- a/pkg/common/oras/authprovider/azure/azureidentity_test.go +++ b/pkg/common/oras/authprovider/azure/azureidentity_test.go @@ -167,6 +167,7 @@ func TestMIAuthProvider_Provide_TokenRefreshSuccess(t *testing.T) { authClientFactory: mockAuthClientFactory, registryHostGetter: mockRegistryHostGetter, getManagedIdentityToken: mockManagedIdentityTokenGetter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method @@ -200,6 +201,7 @@ func TestMIAuthProvider_Provide_TokenRefreshFailure(t *testing.T) { authClientFactory: mockAuthClientFactory, registryHostGetter: mockRegistryHostGetter, getManagedIdentityToken: mockManagedIdentityTokenGetter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method diff --git a/pkg/common/oras/authprovider/azure/azureworkloadidentity.go b/pkg/common/oras/authprovider/azure/azureworkloadidentity.go index 31f45127d..f62643396 100644 --- a/pkg/common/oras/authprovider/azure/azureworkloadidentity.go +++ b/pkg/common/oras/authprovider/azure/azureworkloadidentity.go @@ -72,11 +72,13 @@ type WIAuthProvider struct { registryHostGetter RegistryHostGetter getAADAccessToken AADAccessTokenGetter reportMetrics MetricsReporter + endpoints []string } type azureWIAuthProviderConf struct { - Name string `json:"name"` - ClientID string `json:"clientID,omitempty"` + Name string `json:"name"` + ClientID string `json:"clientID,omitempty"` + Endpoints []string `json:"endpoints,omitempty"` } const ( @@ -113,6 +115,11 @@ func (s *AzureWIProviderFactory) Create(authProviderConfig provider.AuthProvider } } + endpoints, err := parseEndpoints(conf.Endpoints) + if err != nil { + return nil, re.ErrorCodeConfigInvalid.WithError(err) + } + // retrieve an AAD Access token token, err := defaultGetAADAccessToken(context.Background(), tenant, clientID, AADResource) if err != nil { @@ -127,6 +134,7 @@ func (s *AzureWIProviderFactory) Create(authProviderConfig provider.AuthProvider registryHostGetter: &defaultRegistryHostGetterImpl{}, // Concrete implementation getAADAccessToken: &defaultAADAccessTokenGetterImpl{}, // Concrete implementation reportMetrics: &defaultMetricsReporterImpl{}, + endpoints: endpoints, }, nil } @@ -157,6 +165,10 @@ func (d *WIAuthProvider) Provide(ctx context.Context, artifact string) (provider return provider.AuthConfig{}, re.ErrorCodeHostNameInvalid.WithComponentType(re.AuthProvider) } + if err := validateHost(artifactHostName, d.endpoints); err != nil { + return provider.AuthConfig{}, re.ErrorCodeHostNameInvalid.WithError(err) + } + // need to refresh AAD token if it's expired if time.Now().Add(time.Minute * 5).After(d.aadToken.ExpiresOn) { newToken, err := d.getAADAccessToken.GetAADAccessToken(ctx, d.tenantID, d.clientID, AADResource) diff --git a/pkg/common/oras/authprovider/azure/azureworkloadidentity_test.go b/pkg/common/oras/authprovider/azure/azureworkloadidentity_test.go index b2ffaa0cd..93f814cce 100644 --- a/pkg/common/oras/authprovider/azure/azureworkloadidentity_test.go +++ b/pkg/common/oras/authprovider/azure/azureworkloadidentity_test.go @@ -82,6 +82,7 @@ func TestWIAuthProvider_Provide_Success(t *testing.T) { registryHostGetter: mockRegistryHostGetter, getAADAccessToken: mockAADAccessTokenGetter, reportMetrics: mockMetricsReporter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method @@ -126,6 +127,7 @@ func TestWIAuthProvider_Provide_RefreshToken(t *testing.T) { registryHostGetter: mockRegistryHostGetter, getAADAccessToken: mockAADAccessTokenGetter, reportMetrics: mockMetricsReporter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method @@ -161,6 +163,7 @@ func TestWIAuthProvider_Provide_AADTokenFailure(t *testing.T) { registryHostGetter: mockRegistryHostGetter, getAADAccessToken: mockAADAccessTokenGetter, reportMetrics: mockMetricsReporter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method @@ -238,6 +241,7 @@ func TestWIAuthProvider_Provide_TokenRefresh_Success(t *testing.T) { registryHostGetter: mockRegistryHostGetter, getAADAccessToken: mockAADAccessTokenGetter, reportMetrics: mockMetricsReporter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method @@ -273,6 +277,7 @@ func TestWIAuthProvider_Provide_TokenRefreshFailure(t *testing.T) { registryHostGetter: mockRegistryHostGetter, getAADAccessToken: mockAADAccessTokenGetter, reportMetrics: mockMetricsReporter, + endpoints: []string{"example.azurecr.io"}, } // Call Provide method diff --git a/pkg/common/oras/authprovider/azure/const.go b/pkg/common/oras/authprovider/azure/const.go index b311c02a6..1ee843c5f 100644 --- a/pkg/common/oras/authprovider/azure/const.go +++ b/pkg/common/oras/authprovider/azure/const.go @@ -25,6 +25,7 @@ const ( dockerTokenLoginUsernameGUID = "00000000-0000-0000-0000-000000000000" AADResource = "https://containerregistry.azure.net/.default" defaultACRExpiryDuration time.Duration = 3 * time.Hour + defaultACREndpoint = "*.azurecr.io" ) var logOpt = logger.Option{ diff --git a/pkg/common/oras/authprovider/azure/helper.go b/pkg/common/oras/authprovider/azure/helper.go index beafa4db0..38290d581 100644 --- a/pkg/common/oras/authprovider/azure/helper.go +++ b/pkg/common/oras/authprovider/azure/helper.go @@ -17,6 +17,8 @@ package azure import ( "context" + "fmt" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry" provider "github.com/ratify-project/ratify/pkg/common/oras/authprovider" @@ -82,3 +84,54 @@ type defaultRegistryHostGetterImpl struct{} func (g *defaultRegistryHostGetterImpl) GetRegistryHost(artifact string) (string, error) { return provider.GetRegistryHostName(artifact) } + +// parseEndpoints checks if the endpoints are valid for auth provider. If no +// endpoints are provided, it defaults to the default ACR endpoint. +// A valid endpoint is either a fully qualified domain name or a wildcard domain +// name folloiwing RFC 1034. +// Valid examples: +// - *.example.com +// - example.com +// +// Invalid examples: +// - * +// - example.* +// - *example.com.* +// - *. +func parseEndpoints(endpoints []string) ([]string, error) { + if len(endpoints) == 0 { + return []string{defaultACREndpoint}, nil + } + for _, endpoint := range endpoints { + switch strings.Count(endpoint, "*") { + case 0: + continue + case 1: + if !strings.HasPrefix(endpoint, "*.") { + return nil, fmt.Errorf("invalid wildcard domain name: %s, it must start with '*.'", endpoint) + } + if len(endpoint) < 3 { + return nil, fmt.Errorf("invalid wildcard domain name: %s, it must have at least one character after '*.'", endpoint) + } + default: + return nil, fmt.Errorf("invalid wildcard domain name: %s, it must have at most one wildcard character", endpoint) + } + } + return endpoints, nil +} + +// validateHost checks if the host is matching endpoints supported by the auth +// provider. +func validateHost(host string, endpoints []string) error { + for _, endpoint := range endpoints { + if endpoint[0] == '*' { + if _, zone, ok := strings.Cut(host, "."); ok && zone == endpoint[2:] { + return nil + } + } + if host == endpoint { + return nil + } + } + return fmt.Errorf("the artifact host %s is not in the scope of the store auth provider", host) +} diff --git a/pkg/common/oras/authprovider/azure/helper_test.go b/pkg/common/oras/authprovider/azure/helper_test.go index 49c811f0f..87bcfb8d1 100644 --- a/pkg/common/oras/authprovider/azure/helper_test.go +++ b/pkg/common/oras/authprovider/azure/helper_test.go @@ -98,3 +98,93 @@ func TestAuthenticationClientWrapper_ExchangeAADAccessTokenForACRRefreshToken(t _, err := wrapper.ExchangeAADAccessTokenForACRRefreshToken(ctx, grantType, service, options) assert.Nil(t, err) } + +func TestValidateEndpoints(t *testing.T) { + tests := []struct { + name string + endpoint string + expectedErr bool + }{ + { + name: "global wildcard", + endpoint: "*", + expectedErr: true, + }, + { + name: "multiple wildcard", + endpoint: "*.example.*", + expectedErr: true, + }, + { + name: "no subdomain", + endpoint: "*.", + expectedErr: true, + }, + { + name: "full qualified domain", + endpoint: "example.com", + expectedErr: false, + }, + { + name: "valid wildcard domain", + endpoint: "*.example.com", + expectedErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := parseEndpoints([]string{tt.endpoint}) + if tt.expectedErr != (err != nil) { + t.Fatalf("expected error: %v, got error: %v", tt.expectedErr, err) + } + }) + } +} + +func TestValidateHost(t *testing.T) { + endpoints := []string{ + "*.azurecr.io", + "example.azurecr.io", + } + tests := []struct { + name string + host string + expectedErr bool + }{ + { + name: "empty host", + host: "", + expectedErr: true, + }, + { + name: "valid host", + host: "example.azurecr.io", + expectedErr: false, + }, + { + name: "no subdomain", + host: "azurecr.io", + expectedErr: true, + }, + { + name: "multiple subdomains", + host: "example.test.azurecr.io", + expectedErr: true, + }, + { + name: "matched host", + host: "test.azurecr.io", + expectedErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateHost(tt.host, endpoints) + if tt.expectedErr != (err != nil) { + t.Fatalf("expected error: %v, got error: %v", tt.expectedErr, err) + } + }) + } +}