기술 자료

shdctl architecture and security

Learn about shdctl trust boundaries, three usage modes, and what shdctl touches on the network and in your cluster
읽는 시간 12분최근 업데이트: 하루 전

This information helps security teams review shdctl. It describes what shdctl touches on the network, in your cluster, and on disk, and the three ways to use it. Each mode has different trust-boundary implications, so pick the one that matches your security posture.

What shdctl does (full capability map)

shdctl consists of five phases. Only Render (
release generate
) is required; every other phase is optional and you can replace it with your existing tooling. There are also two optional steps outside the phases:
cluster check
, which verifies your cluster meets the deployment prerequisites, and
secret export
, which rebuilds your secrets import file from a running cluster when the original is lost.
The three usage modes that follow are common combinations of these phases.
You can run
cluster check
at any point after the Setup phase. It reads from the cluster and doesn't change it, unless you pass
--probe-storage
, which creates and deletes temporary test PVCs. For more information, refer to shdctl cluster command.
The following are the three usage modes:
  • Mode A (recommended) uses every phase.
  • Mode B is Mode A with the Mirror phase made explicit for air-gapped deployments.
  • Mode C stops after Render; shdctl hands its outputs to the CD tooling you already run.
Mode A runs shdctl only in your CI pipeline. It has two variants, depending on what performs the actual workload deployment:
  • Mode A (ArgoCD variant) — recommended. shdctl renders ArgoCD app-of-apps charts, your pipeline commits them to Git, and shdctl applies a single bootstrap
    Application
    to the cluster. ArgoCD takes over from there. shdctl never holds a cluster-wide deployment credential — ArgoCD does.
  • Mode A (Helm variant). shdctl renders Helm charts and runs
    helm upgrade --install
    directly for every chart in the release. There's no Git commit and no ArgoCD. The CI runner needs a cluster-wide kubeconfig, which is a weaker trust boundary. Use this variant only if you don't run ArgoCD.

Mode A (ArgoCD variant)

Your
manifest.yaml
lives in your Git repo, typically the same repo ArgoCD reads from. The pipeline does the following:
  • Checks out the manifest
  • Validates the manifest and checks the cluster
  • Pulls the release from the Unity registry
  • Optionally mirrors artifacts to your registry
  • Renders charts and Secret manifests
  • Commits the rendered charts to Git
  • Applies the Secrets and a single bootstrap ArgoCD
    Application
    to the cluster
From that point on, ArgoCD pulls charts from your Git repo and deploys workloads. The step numbers below are those of the pipeline steps.
Apart from the read-only
cluster check
, shdctl's cluster touch is limited to two namespace-scoped applies: the Secrets and the bootstrap
Application
. All workload deployment after step 7 in the diagram is ArgoCD pulling from Git. ArgoCD holds the cluster-wide deployment credential, not the CI runner.

Mode A (Helm variant)

If you don't run ArgoCD, shdctl can deploy directly with Helm. Steps 5, 8, and 9 in the diagram from the ArgoCD variant don't apply because there's no Git commit and no ArgoCD reconcile. Step 3 drops
--format argocd
, and step 7 becomes
release deploy --format helm
, which runs
helm upgrade --install
for every chart in the release.
shdctl runs every chart's
helm upgrade --install
from CI. The CI runner kubeconfig must have rights to deploy every chart in the release — cluster-wide, not namespace-scoped. ArgoCD is recommended because it keeps that credential out of CI.

Mode B — Air-gapped mirror

A bastion or staging host runs shdctl once to mirror Unity release artifacts into your private registry. After the mirror completes, the production cluster, ArgoCD, and your Git repository never reach the public internet. Image references in the rendered charts are rewritten to your registry at generate time.
Only the bastion contacts the Unity registry, and only at pull and sync time. After the sync completes, the deployment is fully internal. Same
--format helm | argocd
choice as Mode A; ArgoCD recommended. A Helm deploy stays internal only when the host running it has no Helm chart repository registered; refer to What shdctl touches.
The mirror is scoped to your manifest.
shdctl artifact sync images
copies the images your manifest's enabled features require, not the release's full image set — a deployment with Istio or monitoring turned off does not carry those images into your registry. Two consequences on an air-gapped bastion, where a missing image cannot be fetched at deploy time:
  • Changing a feature toggle means re-running
    shdctl artifact sync images
    before you deploy. The images that feature needs were never mirrored.
  • shdctl artifact sync verify
    reports what a registry is missing
    for a given manifest, and exits non-zero when anything required is absent — so a pipeline can gate a deploy on it. Run it after a sync, and again after any manifest change, before you deploy.
Pass
--all
to
shdctl artifact sync images
to mirror the full image set instead: appropriate for a registry shared by deployments with different feature sets, or where a later feature change must not require another trip to Unity's registry.

Mode C — Generate-only (bring your own CD)

shdctl renders Helm chart values and Kubernetes
Secret
manifests from your
manifest.yaml
and the extracted release package, and then stops. Whatever CD tooling you already use, such as
helm
,
kubectl
, Flux, or custom pipelines, can apply
generated-charts/
and
secrets.yaml
. shdctl never holds a kubeconfig in this mode and never reaches your cluster. This is the minimum viable use of shdctl.
If you want shdctl to run the Helm deploys itself with
shdctl release deploy --format helm
, that isn't Mode C — it's the Mode A (Helm variant) flow described previously, because shdctl then needs a kubeconfig with cluster-wide deploy rights. Direct Helm deploy shows the commands; for the deploy flags, refer to the release command reference.

What shdctl touches

  • Outbound network:
    • Unity registry (
      uccmpprivatecloud.azurecr.io
      ) — required for
      release pull
      and the source side of
      artifact sync
      .
    • Your registry — required for the target side of
      artifact sync
      .
      artifact sync verify
      reads it only: each release manifest the release lists, then one image-manifest lookup per image reference, writing and deleting nothing.
    • Helm chart repositories — only for
      release deploy --format helm
      , and only the repositories registered on the host that runs it (
      helm repo list
      ). Updating each chart's dependencies makes Helm refresh every registered repository, and with
      --concurrency
      above 1 shdctl runs
      helm repo update
      up front. The release's charts resolve their dependencies from the package itself, so a host with no repository registered makes no such call.
    • Kubernetes API — used only for
      secret deploy
      ,
      release deploy
      ,
      release uninstall
      ,
      cluster check
      , and
      secret export
      .
    • shdctl secret export
      : reads secret values —
      kubectl get secret
      across the target namespace, to rebuild the import file from what is deployed. It is the only command that copies secret values out of your cluster into a file. It needs a kubeconfig with the
      list
      verb on secrets in that namespace — an unnamed collection read is a
      list
      , not a
      get
      , so a Role granting only
      get
      is refused with
      cannot list resource "secrets"
      . Either way it is more than the
      get
      /
      create
      /
      patch
      a deployment pipeline needs, so run it as an operator rather than widening a CI role for it. It writes nothing to the cluster, and
      --dry-run
      prints the command without contacting it.
    • shdctl cluster check
      : read-only cluster access, unless you pass
      --probe-storage
      . It runs
      kubectl version
      for the server version, and
      kubectl get
      against nodes, storage classes, namespaces, API services, custom resource definitions, and a cluster-wide pod list (
      -A
      ). The pod list confirms that the ArgoCD controller is running, and the check is reported as skipped if your credentials don't allow it. The command also attempts to read Karpenter node pools (
      nodepools.karpenter.sh
      ), but only when a node pool has no running node, and it tolerates a failure such as a missing custom resource definition or denied access, because Karpenter is optional. With
      --probe-storage
      it additionally creates, waits on and deletes a labeled 1Gi test PVC in the target namespace for each class it probes — the cluster default,
      defaultStorageClass
      when that is a different class, and
      readWriteManyStorageClass
      — and, only on a probe bind failure, reads events in that namespace for diagnostics. A probe that binds provisions a real volume, which a class with
      reclaimPolicy: Retain
      keeps after the PVC is gone. With
      --dry-run
      it touches nothing and prints every call the checks would make. For more information, refer to shdctl cluster command.
  • Credentials read:
    • Unity registry credentials, for
      release pull
      :
      ~/.shdctl/config.json
      , which
      shdctl configure
      writes. The file is readable only by your user but not encrypted. A
      ~/.vpctl/config.json
      left by vpctl, the tool's name before 1.0.0, is also read, never written — and
      shdctl configure delete
      leaves it alone, so delete it yourself once its credentials have moved.
    • Docker credential store (
      ~/.docker/config.json
      ) for
      artifact sync
      : the
      docker
      CLI uses it for images,
      oras-go
      for ORAS artifacts and the release manifests that
      sync images
      reads from the source and
      verify
      from the target.
    • kubeconfig (default discovery and the current context;
      secret deploy
      and
      secret export
      take
      --context
      to name another — for every other command, choose it with
      KUBECONFIG
      or
      kubectl config use-context
      ).
  • Files written (inside the working directory, unless you point an output flag elsewhere):
    • unity-private-cloud-shd-<version>.tar.gz
      — the release archive
      release pull
      downloads (
      --output
      ), kept after extraction.
    • extracted-release/
      — release archive contents.
    • generated-charts/
      — rendered Helm charts and ArgoCD
      Application
      manifests.
    • secrets.yaml
      — rendered Kubernetes
      Secret
      manifests, including the image pull secret named by
      configuration.kubernetes.imagePullSecret
      unless that block sets
      generate: false
      . Written owner-readable only (
      0600
      ), on first write and on every re-run.
    • The persist file — created only when you pass
      --persist <path>
      to
      secret generate
      , at the path you choose. Holds your secrets in the clear, owner-readable only (
      0600
      ).
    • secret export
      's output, at
      --output
      (default
      secrets.import.yaml
      ). Holds your secrets in the clear, owner-readable only (
      0600
      ); an existing file is never overwritten without
      --force
      .
    • manifest.yaml
      — only from
      manifest init
      , at
      --output
      .
    • The CUE schema — only from
      manifest schema --export <path>
      , at that path.
  • Outside the working directory — output flags aside — shdctl writes only the credential file
    shdctl configure
    keeps under your home directory (above), plus two temporary items in the system temp directory, each removed when its command finishes:
    release pull
    's download directory and
    cluster check --probe-storage
    's PVC manifest. The tools it drives keep their own state as usual — Docker's local image store, Helm's cache.
  • What shdctl never does: send telemetry, call back to Unity, auto-update.