Adding a Private Harbor CA Certificate to a VKS Cluster

In my lab I have been building out a small VKS cluster running on the Supervisor in vCenter. The next step was to use a private Harbor registry for container images rather than pulling everything directly from the internet.

Harbor itself was deployed on a Photon OS VM and was reachable at harbor.wynner.ie.

The first issue I hit was the common one with private registries in a lab. Harbor was using a certificate signed by my own CA, so the VKS worker nodes did not automatically trust the HTTPS endpoint.

From Kubernetes this normally appears when a pod tries to pull an image from Harbor. If the worker node does not trust the CA, the image pull fails with an error similar to:

x509: certificate signed by unknown authority

The fix is not to disable TLS checking. The better approach is to add the Harbor CA certificate into the VKS cluster configuration so the nodes trust the registry properly.

The Lab Setup

For this example the Supervisor namespace is:

svc-tkg-domain-c10

The VKS cluster is:

dev-tkc01

The Harbor registry is:

harbor.wynner.ie

The test image I used for validation was:

harbor.wynner.ie/containers/nginx:vks-smoke

Adjust those values for your own environment.

Getting the Harbor CA Certificate

You need the CA certificate that signed the Harbor HTTPS certificate. This is important. You do not want the Harbor server certificate unless that certificate is itself the trusted CA.

If you already have the CA certificate as a file, this command outputs it in a JSON-friendly format with escaped line breaks:

awk 'NF { printf "%s\n", $0 }' harbor-ca.crt

The output should look like this, with
between each line:

-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----

Creating the JSON Patch

Create a file called harbor-ca-trust-patch.json.

[
  {
    "op": "add",
    "path": "/spec/topology/variables/-",
    "value": {
      "name": "osConfiguration",
      "value": {
        "trust": {
          "additionalTrustedCAs": [
            {
              "caCert": {
                "content": "-----BEGIN CERTIFICATE-----\nPASTE_THE_CA_CERTIFICATE_CONTENT_HERE\n-----END CERTIFICATE-----\n"
              }
            }
          ]
        }
      }
    }
  }
]

Replace the content value with the CA certificate from the previous step.

One thing to watch: if the cluster already has an osConfiguration variable, do not add a second one. In that case you should replace the existing variable rather than appending a new one.

Applying the Patch

The patch is applied to the Cluster object in the Supervisor namespace. It is not applied inside Harbor and it is not applied inside the workload cluster.

kubectl --context svc-tkg-domain-c10   -n svc-tkg-domain-c10   patch cluster dev-tkc01   --type=json   --patch-file harbor-ca-trust-patch.json

After applying the patch, VKS rolls the cluster nodes so the new trust configuration is applied. This is expected, so plan for it.

You can monitor the cluster from the Supervisor namespace:

kubectl --context svc-tkg-domain-c10   -n svc-tkg-domain-c10   get cluster dev-tkc01

I waited until the cluster returned to a healthy state:

Ready=True
TopologyReconciled=True

Checking That the Patch Was Applied

You can dump the current Cluster object and confirm the CA is present under osConfiguration.trust.additionalTrustedCAs.

kubectl --context svc-tkg-domain-c10   -n svc-tkg-domain-c10   get cluster dev-tkc01   -o yaml

The relevant section should look similar to this:

spec:
  topology:
    variables:
    - name: osConfiguration
      value:
        trust:
          additionalTrustedCAs:
          - caCert:
              content: |
                -----BEGIN CERTIFICATE-----
                ...
                -----END CERTIFICATE-----

Private Harbor Projects Still Need Pull Credentials

Adding the CA certificate only solves the TLS trust problem. If the Harbor project is private, Kubernetes still needs credentials to pull the image.

Create an image pull secret in the workload namespace:

kubectl --context dev-tkc01   -n my-app-namespace   create secret docker-registry harbor-regcred   --docker-server=harbor.wynner.ie   --docker-username='admin'   --docker-password='PASSWORD'

Then reference it in the pod or deployment:

imagePullSecrets:
  - name: harbor-regcred

Testing the Image Pull

To prove the change worked, I deployed a small test pod using an image stored in Harbor.

The image was:

harbor.wynner.ie/containers/nginx:vks-smoke

The important part was not the application itself, but whether the VKS worker node could pull the image from Harbor over HTTPS.

After the trust patch was applied and the nodes had rolled, the pod events showed:

Pulling image "harbor.wynner.ie/containers/nginx:vks-smoke"
Successfully pulled image "harbor.wynner.ie/containers/nginx:vks-smoke"

That confirmed the VKS node trusted the Harbor certificate chain. If the CA had not been trusted, this is where I would have expected to see the x509 error.

A Note on secretRef

The VKS configuration model also documents a secretRef option for trusted CAs. I initially tested that approach, but in my lab the Cluster API runtime extension failed reconciliation after accepting the patch.

Embedding the CA certificate directly with caCert.content worked cleanly and triggered the expected node rollout.

For that reason, the inline certificate method is what I would use for now unless you have validated secretRef in your own VKS version.

The Lowdown…

  • Add the Harbor CA to the VKS Cluster object, not to Harbor and not manually on each node.
  • Patch the Cluster object in the Supervisor namespace.
  • Expect a node rollout after the change.
  • Use caCert.content with the CA certificate embedded in the cluster configuration.
  • Private Harbor projects still need an imagePullSecret.
  • Test with a real pod pull and check the pod events.

This is a much cleaner approach than trying to bypass TLS validation or manually modifying nodes. It also gives you a repeatable pattern that can be added to your standard VKS cluster YAML for future deployments.

References

Broadcom documents the VKS cluster configuration variables here:

VKS Cluster Configuration Variables

The relevant setting is osConfiguration.trust.additionalTrustedCAs.

Disclaimer!

This was validated in my lab with VKS v1.33.3+vmware.1-fips and a private Harbor registry running on Photon OS.

As always, test this in a non-production environment first. Adding or changing trusted CAs causes a VKS node rollout, so you should plan the timing carefully in any shared or production environment.