Skip to main content
What this is. An attempt to stand up a genuine OpenShift on an in-house bare-metal host, shaped like the pilot subscriber’s environment, with a Harbor transport rig in front of the private channel. The transport half succeeded and is the durable result. The cluster half reached rung 4 of a 4-rung ladder and is therefore not OpenShift — stated here as the headline rather than buried, because a reader skimming for “OpenShift proven” must not find it. PROFILE_TESTED is not flipped by this receipt, and nothing here justifies flipping it.
The subscriber is not named. Their registry hostnames, project names and estate details are not in this document. The rig host is written as <rig-host> and the private channel exercised as <channel>.

The rung, honestly

Why rungs 1–3 were unreachable

The host has no /dev/kvm, and it cannot be given one from the operating system:
SVMDIS=1 under SVM_LOCK=1 is AMD-V disabled in firmware and latched until reset. The kernel clears X86_FEATURE_SVM on that basis. The MSR is write-locked; there is no OS-side path. The remedy is a firmware setting and a reboot — an owner’s act, not a worker’s, and not attempted. QEMU/TCG software emulation was considered and rejected: an OKD single-node bootstrap under pure emulation is a multi-hour proposition on a host that is carrying other people’s live work. Consequence for the ladder as written: three of its four rungs are gated on one firmware bit. Worth knowing before planning around it again — the ladder’s top three are a single point of failure, and the failure is invisible until modprobe runs.

The transport rig — reproduced, and it works

Harbor v2.15.2 as a docker compose, ports 18443/18081 (chosen after checking the host’s running containers and listening sockets), private CA, data and logs under the rig directory rather than /data or /var/log. The four API calls the parity document refers to, exactly:

The nine checks, through the proxy

The cluster pulls through it

containerd on the rig node trusts the rig CA through /etc/containerd/certs.d/<rig-host>:18443/hosts.toml, and both crictl pull and real workload pulls succeed over TLS. The host’s docker daemon was not reconfigured — the trust is on the cluster side, which is also the shape the kit documents (--registry-ca, never REGISTRY_INSECURE). The cluster authenticates to Harbor with a Harbor robot account, not with the channel credential. The subscriber credential exists only in the Harbor endpoint and in a 600 env file on the rig host; it is never handed to the cluster.

Revocation — the one row this rig did NOT verify

The parity matrix records “Revocation behavior — upstream credential rotated — MATCHED, fails closed. This rig could not reproduce that, and does not claim it in either direction. What was established:
  • Harbor refuses to create a registry endpoint whose credential is wrong: 400 BAD_REQUEST — "the registry is unhealthy". That is a genuine fail-closed, at configuration time.
  • The channel itself refuses a wrong secret: 401 on /v2/, on the entry, on revocations, and on the chart path. The signatures/manifests/… path returns 200 anonymously, by design — RUNBOOK § 5.2 makes signature reads credential-free so a stock zero-credential verifier can work.
What could not be established:
  • Mutating the credential on the live endpoint via PUT /api/v2.0/registries/{id} — partial body, then full body, each with and without restarting harbor-core and registry — never produced a refusal. Artifacts in never-before-proxied repositories kept flowing, and the endpoint reported healthy throughout.
A healthy status against a knowingly wrong credential is not self-consistent, so the likeliest reading is that the PUT never applied the secret rather than that Harbor serves without authenticating. But the rig cannot distinguish the two, and the honest test is a real rotation at the channel, which is not a rehearsal act. Recorded as an open question. One adjacent finding, cheap and worth carrying: POST /api/v2.0/registries/ping returned 200 for a payload carrying a knowingly wrong secret, while POST /api/v2.0/registries returned 400 for the same payload. Ping is not an authentication test for this upstream — do not use it as a credential check.

The tenant shape

An isolated Kubernetes v1.30.0 cluster on its own API port and its own kubeconfig. A pre-existing cluster on the same host, carrying unrelated live work, was deliberately not used. Project vexa-dev carries the tenant grant a platform team would issue: restricted-v2 is reproduced as two staged policies — mutate, then reject — under Kyverno v1.19.0, the pin kit/providers/openshift/profile.env carries.

SCC proof, both directions

A pod delivering no securityContext at all — what the chart ships — was admitted and mutated to:
per container: allowPrivilegeEscalation: false, capabilities.drop: [ALL], seccompProfile: RuntimeDefault. Inside the container: uid=1000920000 gid=0(root). A pod that hardened itself out of range was refused:
An in-range explicit runAsUser: 1000920007 was admitted. The operational rule the kit states — deliver no securityContext at all — is the behaviour the rig reproduces. Incidental confirmation: the first admitted pod reports HOME=/. The HOME-under-a-random-UID defect shows up on the very first workload, before Vexa is involved at all.

Argo CD, and what the run added

ARGOCD_VERSION=v3.5.1 namespace-install, into the tenant project.
  • The upstream manifests were refused, exactly as documented. They carry precisely two hard-coded UIDs — runAsUser: 1001 (dex) and runAsUser: 999 (redis) — and the argocd-redis Deployment was rejected at admission: Invalid value: 999: must be in the ranges: [1000920000, 1000929999]. The rejection arrived on the Deployment, not on the Pod, because Kyverno’s autogen rules cover pod controllers. Stripping the two runAsUser lines (a two-line diff over 2 968) admits the whole set, and every Argo pod then runs under the injected project UID.
  • namespace-install.yaml ships no CRDs, and CRDs are cluster-scoped. Argo came up half-dead until they were applied — argocd-server exiting on the server could not find the requested resource (post appprojects.argoproj.io). The tenant kubeconfig is Forbidden on customresourcedefinitions, so this is a platform-team act that the parity document does not currently list among the one-time asks. It belongs on that list.
  • The ~7-pods-of-quota claim is confirmed: 7 pods, 448Mi requested, 8 Services, 8 ConfigMaps, 6 Secrets against the project quota.
  • The OperatorHub / OpenShift GitOps operator route was not available at this rung — it needs OLM, which only a genuine OpenShift or OKD provides. The operator remains recommended on evidence about upstream Argo’s images, still not on a run of it. Unchanged by this receipt.

Tenant scoping — two fidelity gaps found and closed

Both are worth carrying beyond this rig.
  1. kubectl auth can-i reported permissions the API server then refused. It answered yes to get nodes, create namespaces and get clusterpolicies for a ServiceAccount that is in fact Forbidden on all three. Every scope claim in this receipt is from attempting the verb, never from can-i. A rig that had trusted can-i would have reported a broken tenant boundary that was in fact intact — or, worse, the reverse.
  2. Upstream Kubernetes’ admin ClusterRole is not OpenShift’s project admin. It lets the holder patch its own ResourceQuota and delete its own LimitRange; OpenShift’s does not. Left as-is, the rig would have let the tenant widen the very constraint under test. Replaced with a purpose-built role that grants everything edit does plus local RBAC, with quota and limits read-only. A second leak in the same area: the namespaced */*/* Role the parity document says a namespace-scoped Argo needs had been bound to the tenant; it is Argo ServiceAccount material and is now bound only to Argo’s own ServiceAccounts.
After both fixes the tenant can create Deployments, Secrets and RoleBindings in its project, and is refused on nodes, on other namespaces, on cluster-scoped resources, on its own quota and on its own LimitRange.

What this receipt does NOT claim

Stated as gaps, in the register the OpenShift provider README already uses:
  • No OpenShift or OKD was involved. No oc, no SCC objects, no OLM, no Routes, no internal registry, no GitOps operator. restricted-v2 here is a policy engine reproducing a documented behaviour, not the admission controller itself. The 2026-08-21 MicroShift 4.18 rehearsal remains the strongest SCC evidence the project holds; this run does not supersede it and is a weaker rung.
  • kit/install.sh --provider openshift was not run. This receipt covers the environment, not the install. That is the next track.
  • Vexa was not deployed and has not been observed converging. The HOME defect is untouched by anything here.
  • Revocation fail-closed is unverified, per the section above.
  • Route versus LoadBalancer, test/prod policy deltas, general egress beyond the registry, and the cross-project firewall remain exactly as open as they were.

Teardown

The rig is left running, deliberately: it is the environment the install rehearsal runs against next. Nothing on the host outside the rig was modified — the container daemon was not restarted or reconfigured, no images or volumes were pruned, no network configuration was changed, no reboot occurred, and the unrelated workloads sharing the host were untouched throughout. Teardown, when it comes, is: delete the rig cluster, docker compose down in the rig directory, remove the rig directory, and remove the two 600 env files. No state outside those survives.