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 |
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=1to skip the upstream version check.
Install the image mirroring CLI
kcli is a single self-contained bash script. Pin to a specific version tag for reproducible installations.
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:
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
From Cortex, navigate to → .
Select Kubernetes, and then right-click and select Edit.
Click Edit Profile and configure the following:
Set Profile Name to
Standalone-Installer.Disable Auto Upgrade.
Click Apply.
Click Generate.
From the Kubernetes Connect screen, download both
values.yamlandauth.jsonto your computer.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:
REG="myregistry.azurecr.io"
USER="<username>"
PASS="<password-or-token>"
AUTH=$(printf '%s:%s' "$USER" "$PASS" | base64 | tr -d '\n')
cat > pull-secret.dockerconfigjson <<EOF
{"auths":{"$REG":{"auth":"$AUTH"}}}
EOFCredential recipes by registry:
Azure Container Registry (ACR): Use an ACR token with pull rights: <token-name>:<token-password>
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:passwordDocker Hub: Use a Personal Access Token:
<username>:<PAT>
Step 3: Mirror images and rewrite values.yaml
Sign in to your private registry:
docker login myregistry.azurecr.io
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.YYYYMMDDTHHMMSSZand updates three fields inyour values file:global: imageRegistry: myregistry.azurecr.io/cortex dockerPullSecret: <your-private-registry-creds-b64> bundle: <component>: image: repository: <component>Delete the temporary pull secret file:
rm pull-secret.dockerconfigjson
The credentials are now embedded in the rewritten values file under
global.dockerPullSecret.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. jsoncredentials.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 <version>) \ --values <values-file> \ --private-registry <registry> \ (--docker-pull-secret <b64> | --docker-pull-secret-file <file> | --no-pull-secret) \ [--dry-run]
Flag | Required | Description |
|---|---|---|
| One of these | Local konnector Helm chart archive (bundle v2+). |
| One of these | Pull the chart from |
| Yes | Tenant values YAML file. Rewritten in place with a . |
| Yes | Target registry URL (for example, |
| One of these | Base64-encoded dockerconfigjson. |
| One of these | Path to dockerconfigjson file (automatically base64-encoded). |
| One of these | Strip |
| No | Preview changes without pulling, pushing, or rewriting files. |
| 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 to1to skip the upstream version check (air-gapped environments).
Exit codes
0- Success1- Runtime failure64- Usage error65- 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_VERSIONvariable.Installed via git clone: Run
git pullin the cloned repository.
Troubleshooting image mirroring
Issue | Solution |
|---|---|
"Unsupported bundle version" | kcli requires bundle v2 or higher. Verify with |
"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 |
ImagePullBackOff after installation | Most common causes: (1) You used |
Multi-architecture push failures | kcli uses |
"Wrong yq" error | Run |
"A newer kcli version is available" (exit 65) | Update kcli per the Update kcli section, or set |
Debugging | Each run writes a log to |