Prepare the artifacts
Before you start
- Requirements met, tools installed,
yqversion checked - Network and connectivity configured by your network team
- Your registry token obtained from Gen0Sec Support
- Your Gen0Sec API key with the required scopes
0. Set your keys
Create your token files.
mkdir -p ~/.gen0sec
cd ~/.gen0sec/ && touch .registry-token .download-api-key
Then open up an editor of your choice to fill each file with their respective content. Then run
export REGISTRY_TOKEN=$(cat ~/.gen0sec/.registry-token)
export G0S_DOWNLOAD_API_KEY=$(cat ~/.gen0sec/.download-api-key)
By storing keys in files, you make sure they don't get etched into your shell history. If you have history turned off or you know what you're doing, you can also go with
export REGISTRY_TOKEN="your-registry-token"
export G0S_DOWNLOAD_API_KEY="your-gen0sec-api-key"
1. Set the release
Pin the release explicitly. A pinned tag installs the same bytes in six months; a resolved one does not. You can find available release versions on the Releases page.
export TAG=v0.1.0-rc.9
export VERSION=${TAG#v}
To always use the latest version instead:
export TAG=$(curl -fsSL https://releases.gen0sec.com/api/repos/cerebellum/latest | jq -r .tag_name)
[ -n "$TAG" ] && [ "$TAG" != null ] || echo "no full release published yet - pin a tag from the release index instead"
export VERSION=${TAG#v}
2. Download the install kit
curl -fsSLO https://releases.gen0sec.com/cerebellum/$TAG/cerebellum-install-kit.tar.gz
curl -fsSLO https://releases.gen0sec.com/cerebellum/$TAG/cerebellum-install-kit.tar.gz.cosign.bundle
3. Verify the signature
Run this before you unpack anything.
cosign verify-blob \
--bundle cerebellum-install-kit.tar.gz.cosign.bundle \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp '^https://github\.com/gen0sec/cerebellum/\.github/workflows/release\.yaml@refs/tags/' \
cerebellum-install-kit.tar.gz
The output is similar to this:
Verified OK
Anything else means stop. Do not unpack the archive.
cosign refuses to verify without an expected issuer and an expected identity, so a shortened
command fails rather than passing weakly. The identity is pinned to one workflow on a tag reference.
A broader pattern would accept a signature produced by any workflow in our organization. The
signature covers the file's contents, so it proves the bytes are ours regardless of how they reached
you.
4. Unpack it
tar -xzf cerebellum-install-kit.tar.gz
export KIT=$PWD/cerebellum-install-kit-$VERSION
cd $KIT
The kit contains the bundler, the release manifest, the helper scripts used later in this install, and the sizing overlays. It does not contain images or charts.
5. Log in to our registry
export REGISTRY=registry.gen0sec.com
export REGISTRY_USER=any
printf '%s' "$REGISTRY_TOKEN" | helm registry login $REGISTRY -u "$REGISTRY_USER" --password-stdin
printf '%s' "$REGISTRY_TOKEN" | skopeo login $REGISTRY -u "$REGISTRY_USER" --password-stdin
Both are needed. helm and skopeo keep separate credential stores, and the next step uses each of
them.
cosign is a third store again, and it needs no login of its own. The bundler lends it the
credential skopeo holds.
6. Get the charts
- Install from registry
- Create an offline bundle
Pull the charts and their values files. Images are not pulled: your cluster pulls each one from
registry.gen0sec.com when it needs it.
Our registry challenges pulls that send no User-Agent, which on some distributions is what the
kubelet's pull path does. This step succeeds either way, because it runs from your workstation. The
failure appears at step 3 of Install, as a 403 on every image.
Check it now: Known limitations.
./bundler.sh assemble-charts release-manifest.yaml
export B=$PWD/gen0sec-cerebellum-multiarch-$VERSION
The output ends with a per-chart digest confirmation.
assemble-charts checks two digests per chart: the registry's manifest against the digest this
release pins, and the chart archive against the layer named in that manifest. What you install
provably came out of the chart this release published.
Each chart carries its own values-onprem.yaml, extracted into $B/values/. That file is what
points the install at a registry. The default values.yaml leaves the registry blank on purpose, so
installing without -f will not resolve images.
6a. Build the bundle on your workstation
./bundler.sh create-package release-manifest.yaml
This pulls every image and chart the release pins by digest, verifies every signature, then packages
the result as gen0sec-cerebellum-multiarch-$VERSION.tar.gz. It carries both linux/amd64 and
linux/arm64, so it installs on any node in your cluster.
Signature verification happens here, on the connected side, because it needs the public transparency log. Everything downstream is protected by checksums instead.
--arch amd64 or --arch arm64 extracts one manifest out of each multi-architecture index. That
changes every image digest by definition. Per-architecture signatures still verify, because each one
is signed individually. SBOM attestations do not, because they are attached to the index, and the
index is what a single-architecture bundle leaves behind.
The bundler warns at the time and annotates the bundle. If your acceptance process checks attestations, stay on the default. If your nodes are mixed, or you are unsure what they run, stay on the default: a missing architecture fails when the kubelet tries to pull, on an isolated cluster, after the transfer.
A single-architecture bundle also renames the tarball and the directory inside it, so substitute the name in every command that follows.
6b. Move it across
In this step you take your bundle onto an intermediary machine inside the network where your internal registry and the target cluster is located you'll install the bundle onto.
After transferring the bundle, you can clean up all artifacts it has created.
eg.
scp gen0sec-cerebellum-multiarch-$VERSION.tar.gz <target-host>:~/
# `create-package` ran inside the kit directory, so the bundle is sitting in it. Copying the kit first
# would transfer tens of gigabytes twice. `cleanup` removes only what the bundler created.
./bundler.sh cleanup
cd .. && scp -r cerebellum-install-kit-$VERSION <target-host>:~/
# You can even remove the installation kit if you don't intend to keep it.
# cd $KIT/.. && rm -rf cerebellum-install-kit.tar.gz $KIT
6c. Load it into your registry
Log in to your intermediary machine and use the bundler to load the images into your internal registry.
export REGISTRY=registry.internal.example
export REGISTRY_USER=your-registry-user
export REGISTRY_TOKEN=$(cat path/to/your-registry-token)
export KIT=~/cerebellum-install-kit-$VERSION
export VERSION=$(cat $KIT/release-manifest.yaml | yq ".spec.version")
tar -xzf gen0sec-cerebellum-multiarch-$VERSION.tar.gz
export B=~/gen0sec-cerebellum-multiarch-$VERSION
cd $B
printf '%s' "$REGISTRY_TOKEN" | skopeo login $REGISTRY -u "$REGISTRY_USER" --password-stdin
./bundler.sh apply release-manifest.yaml -r $REGISTRY
apply pushes every bundled image into your registry, then rewrites the bundled values files to
point at it. The charts are not pushed: you install them from $B/charts/*.tgz.
The bundle is checked against its own SHA256SUMS before anything is pushed, so a corrupted transfer
fails here rather than at install time.
Set MIRROR_TLS_VERIFY=false before apply. This affects the push only, and it is the one place in
this install where TLS verification can be disabled. Prefer a certificate from your internal CA.
Checkpoint
Whichever method you used, you should now have the following:
| Variable | Points at |
|---|---|
$KIT | The unpacked install kit |
$B | A directory containing charts/ and values/ |
$REGISTRY | The registry your cluster will pull from |
$REGISTRY_USER | The username for that registry (any for ours) |
$REGISTRY_TOKEN | The password or token for that registry |
You are logged in to $REGISTRY. Every page after this one assumes exactly that and nothing else,
which is why nothing after this point branches.