// Module included in the following assemblies:
//
// * openshift_images/image-configuration.adoc

:_mod-docs-content-type: PROCEDURE
[id="images-configuration-registry-mirror-project-secret_{context}"]
= Configuring project-scoped image pull secrets for mirrored registries

[role="_abstract"]
As a cluster administrator, you can edit the cluster-wide `CRIOCredentialProviderConfig` object, named `cluster`, to enable project-scoped image pull secrets that you can use with mirrored repositories. By using project-scoped secrets, you can maintain security isolation between projects without exposing credentials in a global pull secret.

By default, if your cluster uses an `ImageDigestMirrorSet`, `ImageTagMirrorSet`, or `ImageContentSourcePolicy` object to configure repository mirroring, you must use a global pull secret for mirrored registries. You cannot add an image pull secret to a project. However, you can use the cluster-wide `CRIOCredentialProviderConfig` object to configure the kubelet to trigger the CRI-O credential provider, which enables project-scoped image pull secrets.

:FeatureName: Project-scoped image pull secrets for mirrored registries
include::snippets/technology-preview.adoc[]

An administrator edits the `CRIOCredentialProviderConfig` object named `cluster`, to list the registries that a developer can pull from by using a project-scoped secret. The administrator then creates an image pull secret in each namespace where it is needed and configures role-based access control (RBAC) permissions that allow the pod's service account within that namespace to access the secret. The admin can create a different pull secret with different credentials for each namespace or use the same pull secret in multiple namespaces. 

When a developer uses a pod spec in one of those namespaces to pull an image from a listed registry, the `CRIOCredentialProviderConfig` object triggers the CRI-O credential provider. The credential provider resolves mirror configurations, discovers namespace-scoped secrets, and generates short-lived authentication files for CRI-O consumption. This process maintains credential isolation between namespaces while preserving existing mirror configuration methods.

The following is an example `CRIOCredentialProviderConfig` object:

[source,terminal]
----
apiVersion: config.openshift.io/v1alpha1
kind: CRIOCredentialProviderConfig
metadata:
  name: cluster
spec:
  matchImages:
    - "docker.io"
    - "*.example.io"
    - "quay.io"
    - "registry.example.com:5000"
----
where:

`spec.matchImages`:: Specifies the registries available for pulling by using project-scoped secrets. The items in this list trigger the CRI-O credential provider.

Editing this object updates the `CredentialProviderConfig` object on the nodes.

The following is an example `CredentialProviderConfig` object:

[source,terminal]
----
apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
providers:
- apiVersion: credentialprovider.kubelet.k8s.io/v1
  args:
  - get-credentials
  - --v=3
  defaultCacheDuration: 1m0s
  matchImages:
  - gcr.io
  - '*.gcr.io'
  - '*.pkg.dev'
  - container.cloud.google.com
  name: gcr-credential-provider
- apiVersion: credentialprovider.kubelet.k8s.io/v1
  defaultCacheDuration: 1s
  matchImages:
  - '*.example.io'
  - docker.io
  - quay.io
  - registry.example.com:5000
  name: crio-credential-provider
  tokenAttributes:
    cacheType: Token
    requireServiceAccount: false
    serviceAccountTokenAudience: https://kubernetes.default.svc
----
where:

`providers.matchImages`:: Specifies the registries you listed in the `CRIOCredentialProviderConfig` object.
`providers.tokenAttributes`:: Specifies the configuration of the service account token that is passed to the CRI-O credential provider.

Only the `matchImages` parameter is configurable by using the `CRIOCredentialProviderConfig` object. All other parameters are immutable.

In this example, the `crio-credential-provider` configuration was generated from the `CRIOCredentialProviderConfig` object. The `gcr-credential-provider` configuration is a default configuration. 

.Prerequisites

* You have access to the cluster as a user with the `cluster-admin` role.
* You have enabled the `TechPreviewNoUpgrade` feature set in your cluster's `FeatureGate` custom resource (CR). For more information, see "Understanding feature gates".

// The pull secret step 2 is taken from creating-pull-secret
.Procedure

. Create a namespace for the pod by running the following command:
+
[source,terminal]
----
$ oc create namespace <namespace_name>
----
+
Replace `<namespace_name>` with a name for your namespace.

. Create a secret in that namespace from an existing authentication file:
+
.. For Docker clients using `.docker/config.json`, run the following command:
+
[source,terminal]
----
$ oc create secret generic <pull_secret_name> \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson
----
+
Replace `<pull_secret_name>` with a name for your secret and `<path/to/.docker/config.json>` with the path to your Docker `config.json` file.

.. For Podman clients using `.config/containers/auth.json`, run the following command:
+
[source,terminal]
----
$ oc create secret generic <pull_secret_name> \
     --from-file=<path/to/.config/containers/auth.json> \
     --type=kubernetes.io/podmanconfigjson
----
+
Replace `<pull_secret_name>` with a name for your secret and `<path/to/.config/containers/auth.json>` with the path to your Podman `auth.json` file.

. Create a `Role` and a `RoleBinding` object in the namespace to grant service account access to image pull secrets:

.. Create a YAML file similar to the following example:
+
.Example `Role` and `RoleBinding` objects
[source,terminal]
----
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: <role_name>
  namespace: <namespace_name>
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: credential-provider-secret-access-binding
  namespace: <namespace_name>
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: <role_name>
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: system:serviceaccount:<namespace_name>:default
----
+
Replace `<namespace_name>` with a name for your namespace and `<role_name>` with a name for the role.

.. Create the `Role` and `RoleBinding` objects:
+
[source,terminal]
----
$ oc create -f <file_name>.yaml
----

.. Edit the `CRIOCredentialProviderConfig` object:
+
[source,terminal]
----
$ oc edit criocredentialproviderconfig cluster
----

.. Add the `spec.matchImages` stanza with a list of registries similar to the following example:
+
[source,terminal]
----
apiVersion: config.openshift.io/v1alpha1
kind: CRIOCredentialProviderConfig
metadata:
  name: cluster
spec:
  matchImages:
    - "docker.io"
    - "*.example.io"
    - "quay.io"
    - "registry.example.com:5000"
----
where:
+
--
`spec.matchImages`:: Specifies the registries that you want pods to pull from by using a project-scoped secret. Each entry in the list must be a valid fully-qualified domain name with an optional wildcard, port, and path. The maximum length is 512 characters. Wildcards ('*') are supported for full-subdomain labels and top-level domains. Wildcards are not allowed in the port or path portions. The items in this list trigger the CRI-O credential provider. 
--
+
After you save the changes, {product-title} updates the `CredentialProviderConfig` object on the nodes. 

.Verification

. Verify that the `CredentialProviderConfig` object contains the registries from the `matchImages` list:

.. Start a debug session as root for a control plane node by running the following command:
+
[source,terminal]
----
$ oc debug node/<node_name>
----

.. Set `/host` as the root directory within the debug shell:
+
[source,terminal]
----
sh-5.1# chroot /host
----

.. View the `/etc/kubernetes/credential-providers/<platform>-credential-provider.yaml` file:
+
[source,terminal]
----
sh-5.1# cat /etc/kubernetes/credential-providers/<platform>-credential-provider.yaml 
----
+
Replace `<platform>` with one of the following options:
+
--
** Use `ecr` for an {aws-short} cluster.
** Use `gcr` for a {gcp-short} cluster.
** Use `acr` for an {azure-short} cluster.
** Use `generic` for non-cloud platforms.
--
+
The output should include the repositories you added to the `CRIOCredentialProviderConfig` object.
+
[source,terminal]
----
apiVersion: kubelet.config.k8s.io/v1
kind: CredentialProviderConfig
providers:
# ...
- apiVersion: credentialprovider.kubelet.k8s.io/v1
  defaultCacheDuration: 1s
  matchImages:
  - '*.example.io'
  - docker.io
  - quay.io
  - registry.example.com:5000
# ...
----
