Accessing private image repositories

When the image repository visibility is set to private (the default), you need to authenticate before you can pull images. Konflux provides an RBAC image proxy that controls access based on your permissions.

Who can pull images and access scope

Access to pull images is controlled at the tenant level through Kubernetes RBAC:

  • Users or service accounts who have permission to get, list, or watch ImageRepository resources in a tenant can pull images from all components in that tenant

  • Image path format: proxy_host/redhat-user-workloads(-stage)/tenant/component:tag

  • Without the required RBAC permissions, image pulls will fail

To grant this access, a tenant maintainer creates a Role that permits reading ImageRepository resources, then binds that role to a subject with a RoleBinding:

Tenant administrators cannot create new custom Group objects, because groups are managed by the identity provider. You can still bind the Role to an existing group, such as system:authenticated, but for most cases bind it to a ServiceAccount (for automation) or a User (for local development).

Token types and scope

  • User tokens:

    • Any Konflux user can obtain a user token through the proxy authentication flow

    • Issued by the proxy’s Dex (not SSO), so they can ONLY be used for pulling images through the proxy

    • Cannot be used for OpenShift API authentication

    • Expire after 24 hours

    • Best for local development and manual testing

  • Service account tokens:

    • Any Konflux user who has permission to create secrets in a namespace can create a service account token in that namespace

    • Can authenticate against both the proxy AND the OpenShift API (works like a regular OpenShift service account)

    • Do not expire (valid until the secret is deleted)

    • Best for automated systems and CI/CD pipelines

Finding your proxy URL

The examples on this page use image-rbac-proxy.apps.example.com as a placeholder. Replace it with the RBAC image proxy hostname for the cluster that hosts your tenant. The hostname follows this pattern:

image-rbac-proxy.apps.<cluster-domain>

For example, on the stone-prod-p02 cluster the proxy hostname is:

image-rbac-proxy.apps.stone-prod-p02.hjvn.p1.openshiftapps.com

The proxy hostname matches the domain of the Konflux instance you use, so the login command shown in the UI is always the authoritative source for your cluster. To find the exact hostname:

  1. Open the Component details page for any of your components in the Konflux UI.

  2. In the Registry login information section, read the podman login command. The host in that command is the proxy hostname for your cluster.

  3. Copy the private image path shown for the component. It begins with the same proxy hostname.

Granting pull access with a service account

For automated access from external systems such as Testing Farm or CI/CD pipelines, grant a service account permission to pull images. Create the following objects in this order, because each object depends on the ones before it: the Secret and the RoleBinding both reference the ServiceAccount, so the service account must exist first.

  1. Create a Role that permits reading ImageRepository resources:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: repo-viewer
      namespace: <your-namespace>
    rules:
      - apiGroups:
          - appstudio.redhat.com
        resources:
          - imagerepositories
        verbs:
          - get
          - watch
          - list
  2. Create a ServiceAccount:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: external-puller
      namespace: <your-namespace>
  3. Create a Secret that holds a non-expiring token for the service account. The kubernetes.io/service-account.name annotation must reference the service account created in the previous step:

    apiVersion: v1
    kind: Secret
    metadata:
      name: external-puller-token
      namespace: <your-namespace>
      annotations:
        kubernetes.io/service-account.name: external-puller
    type: kubernetes.io/service-account-token
  4. Create a RoleBinding that binds the Role to the ServiceAccount:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: repo-viewer
      namespace: <your-namespace>
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: repo-viewer
    subjects:
      - kind: ServiceAccount
        name: external-puller
        namespace: <your-namespace>
  5. Get the service account token from the secret:

    kubectl get secret external-puller-token -n <your-namespace> -o jsonpath='{.data.token}' | base64 -d
  6. Use the token to authenticate to the registry in your external system:

    podman login -u external-puller image-rbac-proxy.apps.example.com
    # When prompted for password, paste the service account token

    Then pull images:

    podman pull image-rbac-proxy.apps.example.com/redhat-user-workloads/my-tenant/my-app:b153d64
Replace image-rbac-proxy.apps.example.com with your cluster’s proxy hostname. See Finding your proxy URL.
If a token is compromised, delete the secret to revoke the token, then create a new one. The username must match the service account name.

Getting registry login credentials via the UI

To access private images locally with your own user token:

  1. Ask a tenant maintainer to bind the pull Role to your user. This uses the same Role shown in Granting pull access with a service account, with a User subject instead of a ServiceAccount:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: repo-viewer-user
      namespace: <your-namespace>
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: repo-viewer
    subjects:
      - apiGroup: rbac.authorization.k8s.io
        kind: User
        name: <your-username>
  2. Navigate to the Component details page for your component

  3. In the Registry login information section, copy and run the podman login command in your terminal:

    podman login -u unused image-rbac-proxy.apps.example.com
  4. Click the OAuth URL link to get your authentication token

  5. Paste the token when prompted for password

  6. Copy the private image path from the UI and pull the image:

    podman pull image-rbac-proxy.apps.example.com/redhat-user-workloads/my-tenant/my-app:b153d64
    The image path includes the proxy host (e.g., image-rbac-proxy.apps.example.com) which enforces access control. You must use this proxy URL, not a direct registry URL. Replace it with your cluster’s proxy hostname; see Finding your proxy URL.

Using Quay image references for local development

For local development, you can configure Podman to route pulls for quay.io/redhat-user-workloads and quay.io/redhat-user-workloads-stage through the RBAC image proxy. This configuration lets you continue to use existing Quay image references in Podman commands, Containerfiles, and Dockerfiles without changing them.

Before you begin, ensure that a tenant maintainer has granted your user permission to pull images, as described in Getting registry login credentials via the UI. You also need podman, xdg-open, and keyctl installed on your Linux system.

  1. Add the following configuration to ~/.config/containers/registries.conf:

    [[registry]]
    prefix = "quay.io/redhat-user-workloads"
    location = "quay.io/redhat-user-workloads"
    
    [[registry.mirror]]
    location = "image-rbac-proxy.apps.example.com/redhat-user-workloads"
    
    [[registry]]
    prefix = "quay.io/redhat-user-workloads-stage"
    location = "quay.io/redhat-user-workloads-stage"
    
    [[registry.mirror]]
    location = "image-rbac-proxy.apps.example.com/redhat-user-workloads-stage"

    Replace image-rbac-proxy.apps.example.com with your cluster’s proxy hostname. See Finding your proxy URL. Podman now tries the proxy mirror first whenever you pull an image from either Quay organization. If the proxy mirror cannot provide the image, Podman falls back to Quay. For a private image, this fallback succeeds only if you are also authenticated and authorized directly by Quay.

  2. Add the following function to your Bash configuration, such as ~/.bashrc:

    function image_proxy_login {
      local _proxy="image-rbac-proxy.apps.example.com"
      local _key_name="image_proxy_token:${_proxy}"
      local _key_id
      local _token
    
      _key_id=$(keyctl search @s user "${_key_name}" 2>/dev/null)
    
      if [ -n "${_key_id}" ]; then
        echo "Already logged into ${_proxy}"
        return
      fi
    
      echo "Logging into ${_proxy}..."
      xdg-open "https://${_proxy}/oauth" 2>/dev/null
      echo -n "Paste token from browser: "
      read -rs _token
      echo
    
      if ! echo -n "${_token}" | podman login "${_proxy}" -u unused --password-stdin; then
        echo "Login failed" >&2
        return 1
      fi
    
      _key_id=$(echo -n "${_token}" | keyctl padd user "${_key_name}" @s)
      keyctl timeout "${_key_id}" $((8 * 3600))
    }

    Replace image-rbac-proxy.apps.example.com with the same proxy hostname that you used in registries.conf.

  3. Reload your Bash configuration and run the helper:

    source ~/.bashrc
    image_proxy_login

    The helper opens the proxy’s OAuth page, prompts you for the displayed token, and logs Podman into the proxy. After a successful login, it stores the token in your session keyring for eight hours so that repeated calls do not prompt you to authenticate again. The key name includes the proxy hostname so that changing clusters triggers a new login.

  4. Pull the image by using its original Quay reference:

    podman pull quay.io/redhat-user-workloads/my-tenant/my-app:b153d64

    When the proxy mirror provides the image, it verifies your tenant-level RBAC permissions.