# Install on a managed cluster without cluster-admin

This procedure installs Hephaestus on a Kubernetes cluster that other people administer, for example a Rancher-managed RKE2 cluster. You have kubectl access to two namespaces, but you are not a cluster-admin. The cluster's admins apply the cluster-scoped objects of the release, the admin bundle. You install everything else with the Helm chart and the `cluster.yaml` profile.

The release runs the agent-sandbox controller in its own namespace, and every write right of the controller is in a Role of the chart. The pinned controller cannot limit what it reads to one namespace. Thus, the admin bundle grants it one ClusterRole that reads Pods, Services, NetworkPolicies and the four agent-sandbox kinds in all namespaces. The controller can read the specs of the Pods and Services of other namespaces, including their environment values. Each user with the built-in `admin` role in the release namespace can impersonate the controller and read them too. The admins must accept that before they apply the bundle.

## Before you start

The cluster needs these items. Send this list to the admins. The preflight in [Install](#install) checks each item that your access can read.

1. A cluster that the chart supports, Kubernetes 1.35 or later. [Supported clusters](/next/admin/helm-chart.md#supported-clusters) gives the full requirements.
2. A network plugin that enforces NetworkPolicy for egress as well as ingress. RKE2's default, Canal, does.
3. The Gateway API with a controller, and a Gateway listener that accepts HTTPRoutes from the release namespace. It serves the public host name of the release with TLS.
4. Sandbox nodes that meet the [sandbox prerequisites](/next/admin/sandboxes.md#sandbox-prerequisites). They need user namespace support (Linux 6.3 or later, containerd 2.0 or later) and encrypted node storage.
5. A StorageClass for ReadWriteOnce volumes, as the default class or by name, for the PostgreSQL volume: 20 GiB by default (`postgresql.persistence.size`). An [external PostgreSQL](/next/admin/external-database.md) replaces the PostgreSQL volume.
6. Egress on port 443 from the release namespace to the Git providers and the model APIs that the workspaces use.
7. Image pulls from `ghcr.io`, `docker.io` and `registry.k8s.io` on every node.
8. No other agent-sandbox controller on the cluster. A second controller would also act on the release's sandboxes.

Get the release files of the version that you install:

* The chart, which you [verify](/next/admin/kubernetes.md#verify-the-release-chart).
* The k3s bundle `hephaestus-k3s-X.Y.Z.tar.gz` with its signature, which you verify as [Get the release files](/next/admin/k3s.md#1-get-the-release-files) shows. It holds the admin bundle, `hephaestus-cluster-admin.yaml`.
* A checkout of the release's Git tag, from which you run the preflight and the sandbox isolation gate.

On your workstation, you need these tools:

* `kubectl` with a context for the cluster.
* Helm 4, on the `PATH`. The install uses `--wait=watcher`, and the preflight renders the chart with `helm`.
* Node.js at the version that `package.json` of the checkout names. Node.js includes Corepack, which installs the pinned pnpm.

In the directory of your values file, get the checkout and the packages of the preflight:

```
git clone --depth 1 --branch "v$VERSION" https://github.com/hephaestus-build/Hephaestus.git hephaestus-src

(cd hephaestus-src && corepack pnpm install --frozen-lockfile --ignore-scripts)
```

`--ignore-scripts` runs no install script of a package. The preflight does not need one.

## Admin handoff

Give the admins `hephaestus-cluster-admin.yaml` from the verified bundle, and these steps. The admins repeat step 3 for each release.

1. Create the release namespace and the sandbox namespace with the Restricted Pod Security level:

   ```
   kubectl create namespace hephaestus

   kubectl create namespace hephaestus-sandboxes

   kubectl label namespace hephaestus hephaestus-sandboxes pod-security.kubernetes.io/enforce=restricted
   ```

2. Grant the operator the built-in `admin` ClusterRole in both namespaces with a RoleBinding. Also grant the two Roles below with RoleBindings to the operator. `admin` does not cover HTTPRoutes, ResourceQuotas or the agent-sandbox kinds. The chart creates Roles that use these rights. Kubernetes lets a user create a Role only with the rights that the user holds. The quota rule changes only the three quotas of the release, so the admins keep their own quotas.

   ```
   apiVersion: rbac.authorization.k8s.io/v1

   kind: Role

   metadata: { name: hephaestus-operator, namespace: hephaestus }

   rules:

     - apiGroups: [gateway.networking.k8s.io]

       resources: [httproutes]

       verbs: [get, list, watch, create, update, patch, delete]

   ---

   apiVersion: rbac.authorization.k8s.io/v1

   kind: Role

   metadata: { name: hephaestus-operator, namespace: hephaestus-sandboxes }

   rules:

     - apiGroups: [""]

       resources: [resourcequotas]

       verbs: [get, list, watch, create]

     - apiGroups: [""]

       resources: [resourcequotas]

       resourceNames:

         - hephaestus-sandbox-quota

         - hephaestus-sandbox-quota-reviews

         - hephaestus-sandbox-quota-conversations

       verbs: [update, patch, delete]

     - apiGroups: [agents.x-k8s.io]

       resources: [sandboxes, sandboxes/status, sandboxes/finalizers]

       verbs: [get, list, watch, create, update, patch, delete]

     - apiGroups: [extensions.agents.x-k8s.io]

       resources:

         - sandboxclaims

         - sandboxclaims/status

         - sandboxclaims/finalizers

         - sandboxtemplates

         - sandboxtemplates/finalizers

         - sandboxwarmpools

         - sandboxwarmpools/status

         - sandboxwarmpools/finalizers

       verbs: [get, list, watch, create, update, patch, delete]
   ```

   With `observability.stack.enabled` or alert rules, the operator also needs the Prometheus Operator kinds that they use. The preflight names them.

   The [sandbox isolation gate](/next/admin/sandboxes.md#sandbox-isolation-gate) also reads the `kubernetes` Service and its EndpointSlices in `default`, and the cluster DNS Service. Grant these two Roles to the operator with RoleBindings too. Change the name and the namespace of the DNS Service to those of the cluster:

   ```
   apiVersion: rbac.authorization.k8s.io/v1

   kind: Role

   metadata: { name: hephaestus-isolation-gate, namespace: default }

   rules:

     - apiGroups: [""]

       resources: [services]

       resourceNames: [kubernetes]

       verbs: [get]

     - apiGroups: [discovery.k8s.io]

       resources: [endpointslices]

       verbs: [list]

   ---

   apiVersion: rbac.authorization.k8s.io/v1

   kind: Role

   metadata: { name: hephaestus-isolation-gate, namespace: kube-system }

   rules:

     - apiGroups: [""]

       resources: [services]

       resourceNames: [rke2-coredns-rke2-coredns]

       verbs: [get]
   ```

3. Apply the admin bundle:

   ```
   kubectl apply --server-side -f hephaestus-cluster-admin.yaml
   ```

   It holds the four agent-sandbox CRDs, the three PriorityClasses of the release, and the read-only ClusterRole `hephaestus-agent-sandbox-read` with its ClusterRoleBinding. It holds nothing else.

   The ClusterRoleBinding names the controller's ServiceAccount in the namespace `hephaestus`. For another release namespace, do not edit the signed bundle. Bind the ClusterRole to the ServiceAccount `hephaestus-agent-sandbox-controller` of that namespace instead. At each apply, run these commands instead of the command above:

   ```
   kubectl delete clusterrolebinding hephaestus-agent-sandbox-read --ignore-not-found

   kubectl apply --server-side -f hephaestus-cluster-admin.yaml

   kubectl delete clusterrolebinding hephaestus-agent-sandbox-read

   kubectl create clusterrolebinding hephaestus-agent-sandbox-read \

     --clusterrole hephaestus-agent-sandbox-read --serviceaccount <namespace>:hephaestus-agent-sandbox-controller
   ```

   The first command removes your earlier binding, so that the apply does not conflict with it.

4. Tell the operator these facts:

   * The name, namespace and listener of the Gateway, and the Pod labels of its data plane.
   * The address and port of the Kubernetes API as the network plugin sees it, from `kubectl get endpointslices --namespace default --selector kubernetes.io/service-name=kubernetes`.
   * The name of the cluster DNS Service. On RKE2 it is `kube-system/rke2-coredns-rke2-coredns`.
   * The StorageClass, if it is not the default class.
   * If sandboxes must run on dedicated nodes: the node selector and the tolerations of those nodes.

## Install

1. Create the release Secret and the PostgreSQL TLS Secret in the release namespace. Follow [Create the Secret](/next/admin/kubernetes.md#create-the-secret) and [Create the PostgreSQL TLS Secret](/next/admin/kubernetes.md#postgresql-tls-secret). You have `hephaestus-keys.sh` from the k3s bundle.

2. Write your values file, `site.yaml`, as [Write the values file](/next/admin/kubernetes.md#write-the-values-file) describes for `host`, `trustedProxies`, the Secrets and the login provider. Add the facts from the admins:

   site.yaml

   ```
   route:

     parentRefs:

       - name: cluster-gateway

         namespace: gateway-system

         sectionName: https

   networkPolicy:

     gateway:

       namespace: gateway-system

       podLabels:

         app.kubernetes.io/name: gateway

     kubernetesApi:

       cidr: 192.168.10.11/32

       port: 6443

   sandbox:

     namespace: hephaestus-sandboxes
   ```

   `kubernetesApi` is an address from the EndpointSlice, not the address of the `kubernetes` Service ([Network policy](/next/admin/helm-chart.md#network-policy)). A cluster with three server nodes has three addresses: set a CIDR that holds all of them. Use `/128` for one IPv6 address.

   If the admins gave a node selector and tolerations for the sandbox nodes, set them as `sandbox.runtime.nodeSelector` and `sandbox.runtime.tolerations`.

3. Run the preflight in the directory of your values file:

   ```
   node hephaestus-src/scripts/cluster-preflight.ts --namespace hephaestus \

     --values "chart-$VERSION/hephaestus/values.yaml" \

     --values "chart-$VERSION/hephaestus/profiles/cluster.yaml" \

     --values site.yaml
   ```

   The chart of the checkout names no release images, so the first file gives them from the verified chart. The second file applies the cluster profile again after it. The preflight renders the chart with these files, and checks the cluster with your own access. It does not change the cluster.

   Each `missing:` line names one item that the install needs. Forward it to the admins as it is. Each `unverified:` line names a fact that your access cannot read. Ask the admins to confirm it. The preflight fails while one `missing:` line remains.

   The preflight checks the controller's read-only ClusterRole as the controller's ServiceAccount, with `kubectl auth can-i --as`. Without the right to impersonate that account, it compares the ClusterRole and its binding with the admin bundle instead.

4. Install the verified chart with the cluster profile and your values:

   ```
   helm upgrade --install hephaestus "chart-$VERSION/hephaestus" -n hephaestus \

     -f "chart-$VERSION/hephaestus/profiles/cluster.yaml" -f site.yaml \

     --wait=watcher --wait-for-jobs --timeout 45m
   ```

   The profile runs two servers, twelve reviews at once across the sandbox nodes, and the agent-sandbox controller in the release namespace. It names the PriorityClasses of the admin bundle, so the chart creates no cluster-scoped object. [Sizing and scaling](/next/admin/sizing.md#profiles-at-a-glance) gives its requests.

## Check that it worked

1. Run the preflight again after the install, with `--installed`:

   ```
   node hephaestus-src/scripts/cluster-preflight.ts --namespace hephaestus \

     --values "chart-$VERSION/hephaestus/values.yaml" \

     --values "chart-$VERSION/hephaestus/profiles/cluster.yaml" \

     --values site.yaml --installed
   ```

   It also waits up to 10 minutes for the server rollout. The last line starts with `The preflight of hephaestus passed`. If the server is not ready, a `missing:` line gives the command that shows its log.

2. Run the [sandbox isolation gate](/next/admin/sandboxes.md#sandbox-isolation-gate) for each template. Add the cluster's DNS Service, for example `--dns kube-system/rke2-coredns-rke2-coredns` on RKE2. The rights of step 2 of the admin handoff cover the gate. Each run must end with `The sandbox isolation gate passed`.

3. Open the public host name, sign in, and request a review of one pull request.

## Undo

1. Delete the sandbox claims while the agent-sandbox controller still runs:

   ```
   kubectl -n hephaestus-sandboxes delete sandboxclaims --all
   ```

2. Uninstall the release:

   ```
   helm uninstall hephaestus -n hephaestus
   ```

3. Optional: delete the PostgreSQL volume claim, which the uninstall keeps.

   CAUTION: Data loss

   This deletes the database. Keep a [backup](/next/admin/backup-restore.md#back-up-the-database) and the [required keys](/next/admin/secret-custody.md#keep-the-required-keys-outside-the-cluster) if you can restore later.

   ```
   kubectl -n hephaestus delete pvc data-hephaestus-postgresql-0
   ```

4. Ask the admins to delete the two namespaces, which also deletes the release Secret and your RoleBindings:

   ```
   kubectl delete namespace hephaestus hephaestus-sandboxes
   ```

   Also ask them to delete the Roles `hephaestus-isolation-gate` and their RoleBindings in `default` and in the namespace of the DNS Service.

5. Ask the admins to remove the admin bundle of the release:

   ```
   kubectl delete -f hephaestus-cluster-admin.yaml
   ```

   This deletes the agent-sandbox CRDs, the three PriorityClasses and the read-only ClusterRole with its binding.
