> For the complete documentation index, see [llms.txt](https://cortex-docs.paloaltonetworks.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cortex-docs.paloaltonetworks.com/kubernetes-security/mirror-connector-images-to-a-private-registry/use-kcli-to-mirror-connector-images.md).

# Use kcli to mirror connector images

kcli is designed for environments requiring image mirroring before deployment, including air-gapped clusters and GitOps-driven installations. The tool integrates into the Cortex Cloud portal's Kubernetes Connect wizard, slotting between the configuration download step and the final installation step.

### Functional responsibilities

The image mirroring workflow distributes tasks across two roles:

* **Platform Engineer (Infrastructure)**: Installs kcli, mirrors images to the private registry, and commits the rewritten values file to version control.
* **Cluster Administrator (Deployment)**: Executes the portal's installation commands against the rewritten values file to deploy the konnector to the cluster.

### Prerequisites

Verify the following tools are installed on the workstation where you will run kcli:

| Tool        | Minimum version    | Purpose                                      |
| ----------- | ------------------ | -------------------------------------------- |
| bash        | 4.0+               | Script runtime                               |
| docker      | 20.10+ with buildx | Pull and push multi-architecture images      |
| yq          | v4 (mikefarah/yq)  | YAML processing                              |
| jq          | 1.6+               | JSON processing                              |
| curl        | Any recent version | Chart download and version check             |
| helm        | 3.8+               | Pull chart from source OCI registry          |
| tar, base64 | POSIX standard     | Bundled with all supported operating systems |

{% hint style="info" %}
**Note**

* **macOS**: The system bash is version 3.2. Install a newer version with `brew install bash`. kcli will automatically use the Homebrew bash if it is first in your PATH.
* **yq**: Must be the Go-based mikefarah/yq, not the Python variant. Verify with `yq --version`.
* **Network access**: The workstation running kcli must have access to both the Cortex source registry and your private registry. The cluster only needs access to your private registry.
* **Bundle version**: kcli requires konnector bundle v2 or higher. Older bundles are rejected at startup.
* **Air-gapped environments**: Set `KCLI_SKIP_VERSION_CHECK=1` to skip the upstream version check.
  {% endhint %}

### Install the image mirroring CLI

kcli is a single self-contained bash script. Pin to a specific version tag for reproducible installations.

```bash
curl -fsSL "https://raw.githubusercontent.com/PaloAltoNetworks/cortex-cloud/main/tools/kcli/kcli" -o /tmp/kcli
sudo install -m 0755 /tmp/kcli /usr/local/bin/kcli
kcli version
```

Alternatively, clone the repository and create a symlink to the kcli script in your PATH:

```bash
git clone https://github.com/PaloAltoNetworks/cortex-cloud.git
ln -s $(pwd)/cortex-cloud/tools/kcli/kcli /usr/local/bin/kcli
```

### Mirror images to your private registry

#### Step 1: Download configuration files from the portal

1. From Cortex, navigate to Settings → Data Sources & Integrations.
2. Select Kubernetes, and then right-click and select Edit.
3. Click Edit Profile and configure the following:
   * Set Profile Name to `Standalone-Installer`.
   * Disable Auto Upgrade.
   * Click Apply.
4. Click Generate.
5. From the Kubernetes Connect screen, download both `values.yaml` and `auth.json` to your computer.
6. Do not continue with the steps in the wizard. This should be done later when deploying the konnector.

#### Step 2: Create a pull secret for your private registry

If you are using `--docker-pull-secret-file`, create a kubelet-compatible pull secret with inline credentials. kubelet cannot use Docker credential helpers (credsStore, credHelpers, osxkeychain, acr, gcloud, ecr-login).

**Build the pull secret**:

```bash
REG="myregistry.azurecr.io"
USER=""
PASS=""
AUTH=$(printf '%s:%s' "$USER" "$PASS" | base64 | tr -d '\n')
cat > pull-secret.dockerconfigjson <<EOF
{"auths":{"$REG":{"auth":"$AUTH"}}}
EOF
```

**Credential recipes by registry**:

* **Azure Container Registry (ACR)**: Use an ACR token with pull rights: :
* **Google Artifact Registry (GAR) / Google Container Registry (GCR)**: Use a service account with `roles/artifactregistry.reader: _json_key:$(cat key.json)`
* **Amazon Elastic Container Registry (ECR)**: Prefer IRSA or EKS Pod Identity with `--no-pull-secret` (ECR tokens expire after 12 hours).
* **Harbor, Quay, Artifactory**: Use a robot account: `username:password`
* **Docker Hub**: Use a Personal Access Token: `<username>:<PAT>`

#### Step 3: Mirror images and rewrite values.yaml

1. Sign in to your private registry: docker login myregistry.azurecr.io
2. Run kcli mirror to pull images from the Cortex source registry and push them to your private registry: kcli mirror \ --chart-version 2.0.0 \ --values ./values.yaml \ --private-registry myregistry.azurecr.io/cortex \ --docker-pull-secret-file ./pull-secret.dockerconfigjson ### Note If you have already downloaded the Helm chart archive (for example, on an air-gapped relay host), pass the local chart file instead: kcli mirror \ --chart ./konnector-2.0.0.tgz \ --values ./values.yaml \ --private-registry myregistry.azurecr.io/cortex \ --docker-pull-secret-file ./pull-secret.dockerconfigjson**What kcli modifies**: kcli writes a timestamped backup to `<values>.bak.YYYYMMDDTHHMMSSZ` and updates three fields inyour values file: global: imageRegistry: myregistry.azurecr.io/cortex dockerPullSecret: bundle: : image: repository:
3. Delete the temporary pull secret file: rm pull-secret.dockerconfigjson The credentials are now embedded in the rewritten values file under `global.dockerPullSecret`.
4. Commit the rewritten values file to version control if you are using GitOps (Argo CD, Flux): git add values.yaml git commit -m "Mirror konnector images to private registry" git push

#### Step 4: Deploy the Konnector

From the Kubernetes Connect screen, continue at step 2 where you run deployment script and install the Helm chart. This block contains two commands:

* **helm registry login**: Authenticates to the Cortex source OCI registry using the `auth. json` credentials.
* **helm upgrade --install**: Deploys the konnector using the rewritten values file, so Pods pull images from your private registry.

Run both commands together as shown on the screen:

cat K8s-security-profile-panw-31-May-26-5PM.auth.json | helm registry login us-central1-docker.pkg.dev --username \_json\_key --password-stdin helm upgrade --install konnector oci://us-central1-docker.pkg.dev/qa2-test- 99924894351125/agent-docker/helm/konnector-launcher \ --wait-for-jobs \ --create-namespace \ --namespace panw \ --values K8s-security-profile-panw-31-May-26-5PM.values.yaml \ --set-file konnector-upgrader.rawValuesContent=K8s-security-profile-panw- 31-May-26-5PM.values.yaml

### kcli command reference

#### kcli mirror

Mirror images and rewrite the Helm values file.

kcli mirror (--chart \<chart.tgz> | --chart-version ) \ --values \ --private-registry \ (--docker-pull-secret | --docker-pull-secret-file | --no-pull-secret) \ \[--dry-run]

| Flag                               | Required     | Description                                                                                                                                                                                |
| ---------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--chart<chart.tgz>`               | One of these | Local konnector Helm chart archive (bundle v2+).                                                                                                                                           |
| `--chart-version<version>`         | One of these | Pull the chart from `oci://<global.imageRegistry>/helm/konnector-bundle:<version>` (for example, `2.0.0`). The `<global.imageRegistry>` value is read from the values file. Requires helm. |
| `--values<file>`                   | Yes          | Tenant values YAML file. Rewritten in place with a .`bak` backup.                                                                                                                          |
| `--private-registry<url>`          | Yes          | Target registry URL (for example, `myregistry.azurecr.io/proj/repo`).                                                                                                                      |
| `--docker-pull-secret<b64>`        | One of these | Base64-encoded dockerconfigjson.                                                                                                                                                           |
| `--docker-pull-secret-file <file>` | One of these | Path to dockerconfigjson file (automatically base64-encoded).                                                                                                                              |
| `--no-pull-secret`                 | One of these | Strip `global.dockerPullSecret` from values. Use for managed identities (IRSA, AKS, GKE) or out-of-band imagePullSecret resources.                                                         |
| `--dry-run`                        | No           | Preview changes without pulling, pushing, or rewriting files.                                                                                                                              |
| `-h, --help`                       | No           | Display command help.                                                                                                                                                                      |

#### Other commands

* `kcli version` (also `--version`, `-v`): Display the installed kcli version.
* `kcli help` (also `--help`, `-h`): Display general help.
* `kcli mirror --help`: Display help for the mirror command.

#### Environment variables

* `NO_COLOR`: Set to any value to disable ANSI color output.
* `DOCKER_CONFIG`: Docker configuration directory (default: `~/.docker`).
* `TMPDIR`: Temporary directory for chart extraction and logs (default: `/tmp`).
* `KCLI_SKIP_VERSION_CHECK`: Set to `1` to skip the upstream version check (air-gapped environments).

### Exit codes

* `0` - Success
* `1` - Runtime failure
* `64` - Usage error
* `65` - Stale version (kcli is outdated)
* `130` - Interupted by user

### Update kcli

Every kcli run (except `version` and `help`) compares your local version to the canonical script on the main branch. If your version is older, kcli fails with exit code `65` and an actionable message.

The version check is best-effort and is skipped (with a warning) if curl is unavailable, the fetch times out (5 seconds), or `KCLI_SKIP_VERSION_CHECK=1` is set.

#### To update

* **Installed via curl**: Re-run the install command and update the `KCLI_VERSION` variable.
* **Installed via git clone**: Run `git pull` in the cloned repository.

### Troubleshooting image mirroring

| Issue                                                            | Solution                                                                                                                                                                                                                                                                                                                   |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| "Unsupported bundle version"                                     | kcli requires bundle v2 or higher. Verify with `tar -xzOf <chart.tgz> konnector-bundle/Chart.yaml \| yq '.version'`. Download a v2+ chart from the Cortex Cloud portal.                                                                                                                                                    |
| "Could not extract global.imageRegistry/global.dockerPullSecret" | Your values file is missing required fields. The portal-issued file includes both. Verify you are not using a hand-crafted values file.                                                                                                                                                                                    |
| Docker login failures                                            | Source registry credentials come from `global.dockerPullSecret` (must be valid base64). For the target registry, run `docker login <your-registry>` first. In CI/CD, use non-interactive login.                                                                                                                            |
| ImagePullBackOff after installation                              | Most common causes: (1) You used `--docker-pull-secret-file` with `~/.docker/config.json` that delegates to a credential helper. Build a proper inline secret per the pull secret section. (2) You used `--no-pull-secret` but did not create the imagePullSecret resource, or your managed-identity binding is incorrect. |
| Multi-architecture push failures                                 | kcli uses `docker buildx imagetools create` and falls back to single-architecture `docker tag` and docker push. Ensure docker buildx is installed and a builder exists: `docker buildx create --use`.                                                                                                                      |
| "Wrong yq" error                                                 | Run `yq --version`. Must show mikefarah/yq v4. Install with `brew install yq` (macOS) or follow the mikefarah/yq installation guide.                                                                                                                                                                                       |
| "A newer kcli version is available" (exit 65)                    | Update kcli per the Update kcli section, or set `KCLI_SKIP_VERSION_CHECK=1` in air-gapped environments.                                                                                                                                                                                                                    |
| Debugging                                                        | Each run writes a log to `$TMPDIR/kcli-log-XXXXXX.log` (preserved on success and failure). Attach this log to support tickets. For deep debugging, run `bash -x /usr/local/bin/kcli mirror ...`                                                                                                                            |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://cortex-docs.paloaltonetworks.com/kubernetes-security/mirror-connector-images-to-a-private-registry/use-kcli-to-mirror-connector-images.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
