Commit d0a9398f authored by Nikos Psaromanolakis's avatar Nikos Psaromanolakis
Browse files

Edit README.md

parent f624dfce
Loading
Loading
Loading
Loading
+236 −229
Original line number Diff line number Diff line
# ocm-hub
# ocm-managed

Helm chart for deploying and configuring an **Open Cluster Management (OCM) hub** with integrated **OpenZiti tunneling**.
Helm chart for registering a Kubernetes cluster as an **Open Cluster Management (OCM) managed cluster** with integrated **OpenZiti tunneling**.

## Features

* Initializes the OCM hub with `clusteradm init`.
* Pre-creates configured `ManagedCluster` resources.
* Automatically accepts managed-cluster registration CSRs.
* Creates and enrolls the OpenZiti hub identity.
* Retrieves OCM registration information from the hub.
* Executes `clusteradm join`.
* Installs the OCM Klusterlet agents.
* Creates and enrolls an OpenZiti identity for the managed cluster.
* Deploys the OpenZiti `ziti-edge-tunnel`.
* Creates the OpenZiti `ocm-hub-api` service used to expose the hub Kubernetes API.
* Reconciles the configured managed-cluster list on Helm upgrades.
* Uses a pre-existing Kubernetes Secret containing the hub kubeconfig.
* Checks direct connectivity to the hub Kubernetes API.
* Creates a cluster-specific OpenZiti Dial policy when direct hub connectivity is unavailable.
* Cleans up OCM and OpenZiti resources during Helm uninstall.

## Deployment Flow
## Registration Flow

```text
Helm install ocm-hub
User / Orchestrator
        |
        | creates Secret beforehand
        v
Secret: ocm-hub-kubeconfig
        |
        | key: kubeconfig
        v
Helm install ocm-managed
        |
        v
OpenZiti bootstrap
        |
        +--> Create hub-ziti identity
        +--> Create cluster1-ziti identity
        +--> Retrieve enrollment JWT
        +--> Enroll identity
        +--> Create Kubernetes identity Secret
@@ -30,47 +39,46 @@ OpenZiti bootstrap
Start ziti-edge-tunnel
        |
        v
Create OpenZiti hub API service
Mount existing hub kubeconfig Secret
        |
        +--> Create intercept.v1
        +--> Create host.v1
        +--> Create Bind policy
        v
Read hub API endpoint
        |
        v
Initialize OCM hub
clusteradm init --wait
Check direct hub connectivity
        |
        +-----------------------------+
        |                             |
     reachable                    unreachable
        |                             |
        |                             v
        |                    Authenticate to OpenZiti
        |                             |
        |                             v
        |                    Create Dial policy
        |                             |
        +-------------+---------------+
                      |
                      v
OCM Hub Control Plane
              clusteradm get token
                      |
                      v
Create configured ManagedCluster resources
Create temporary kubeconfig for local managed cluster
                      |
                      v
Managed cluster sends registration CSR
                clusteradm join
                      |
                      v
Automatic CSR acceptance
               Install Klusterlet
                      |
                      v
Managed cluster becomes
JOINED=True / AVAILABLE=True
```

The chart combines two responsibilities:

```text
OCM
             Registration CSR -> Hub
                      |
 +--> cluster registration
 +--> lifecycle management
 +--> ManagedCluster reconciliation

OpenZiti
                      v
              Registration accepted
                      |
 +--> secure overlay connectivity
 +--> hub identity management
 +--> hub API service exposure
                      v
              Managed cluster ready
```

## OpenZiti Dependency
@@ -96,8 +104,8 @@ helm dependency update
Example `values.yaml`:

```yaml
managedClusters:
  - cluster1
clusterName: cluster1
hubKubeconfigSecretName: "ocm-hub-kubeconfig"

openziti:
  enabled: true
@@ -108,8 +116,8 @@ openziti:
    password: "<OPENZITI-PASSWORD>"

  identity:
    name: "hub-ziti"
    secretName: "ziti-hub-identity"
    name: "cluster1-ziti"
    secretName: "ziti-cluster1-identity"

  hubApi:
    serviceName: "ocm-hub-api"
@@ -122,7 +130,7 @@ ziti-edge-tunnel:
    tag: "1.1.5"

  secret:
    existingSecretName: "ziti-hub-identity"
    existingSecretName: "ziti-cluster1-identity"
    keyName: persisted-identity

  tolerations:
@@ -132,152 +140,147 @@ ziti-edge-tunnel:
### Configuration Reference

| Value                                        | Description                                                                              |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `managedClusters`                            | List of managed clusters that should exist on the OCM hub. The chart creates missing entries and removes entries that are no longer configured. |
| -------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `clusterName`                                | Name under which this Kubernetes cluster is registered in OCM.                           |
| `hubKubeconfigSecretName`                    | Name of the pre-existing Kubernetes Secret containing the hub kubeconfig.                |
| `openziti.enabled`                           | Enables the OpenZiti integration and `ziti-edge-tunnel` dependency.                      |
| `openziti.controller.url`                    | OpenZiti Controller management API endpoint.                                             |
| `openziti.controller.username`               | Username used by the bootstrap and configuration Jobs to authenticate to the OpenZiti Controller.                                               |
| `openziti.controller.username`               | Username used by the bootstrap and join Jobs to authenticate to the OpenZiti Controller. |
| `openziti.controller.password`               | Password used to authenticate to the OpenZiti Controller.                                |
| `openziti.identity.name`                     | OpenZiti identity created for the hub cluster.                                                                                                  |
| `openziti.identity.name`                     | OpenZiti identity created for this managed cluster.                                      |
| `openziti.identity.secretName`               | Kubernetes Secret where the enrolled OpenZiti identity JSON is stored.                   |
| `openziti.hubApi.serviceName`                | Name of the OpenZiti service used to expose the hub Kubernetes API.                                                                             |
| `openziti.hubApi.serviceName`                | Name of the OpenZiti service exposing the OCM hub Kubernetes API.                        |
| `ziti-edge-tunnel.image.*`                   | Container image configuration for the OpenZiti tunneler.                                 |
| `ziti-edge-tunnel.secret.existingSecretName` | Secret mounted by the tunneler. This should match `openziti.identity.secretName`.        |
| `ziti-edge-tunnel.secret.keyName`            | Key inside the Secret containing the OpenZiti identity JSON.                             |

The following values must reference the same Kubernetes Secret:
## Hub Kubeconfig Secret

```text
openziti.identity.secretName
              |
              +----> generated by bootstrap Job
The chart no longer receives the hub kubeconfig directly through Helm values.

ziti-edge-tunnel.secret.existingSecretName
              |
              +----> consumed by ziti-edge-tunnel
Instead, the kubeconfig must already exist in the managed cluster as a Kubernetes Secret.

The Secret must:

* exist in the same namespace where the Helm chart is installed;
* use the name configured in `hubKubeconfigSecretName`;
* contain the kubeconfig under the key `kubeconfig`.

Example:

```bash
kubectl create namespace ocm-managed
```

## OpenZiti Bootstrap
Create the Secret:

During the initial Helm installation, the chart automatically creates the OpenZiti identity required by the hub.
```bash
kubectl create secret generic ocm-hub-kubeconfig \
  -n ocm-managed \
  --from-file=kubeconfig=hub-kubeconfig.yaml
```

The bootstrap process is:
Verify it:

```text
Authenticate to OpenZiti Controller
        |
        v
Create hub-ziti identity
        |
        v
Retrieve enrollment JWT
        |
        v
Enroll identity
        |
        v
Generate identity.json
        |
        v
Create Kubernetes Secret
ziti-hub-identity
        |
        v
Start ziti-edge-tunnel
```bash
kubectl get secret ocm-hub-kubeconfig -n ocm-managed
```

The generated identity JSON is stored in the Kubernetes Secret under the key:
The chart mounts the Secret inside the join Job as:

```text
persisted-identity
/hub/kubeconfig
```

The `ziti-edge-tunnel` subchart mounts this Secret and starts the tunneler using the enrolled hub identity.
The chart does not create or delete this Secret. It is considered externally managed.

## OCM Hub Initialization
## Kubeconfig Usage

After the OpenZiti tunneler is ready, the chart initializes the OCM hub using:
Two kubeconfigs are used during the registration process.

```bash
clusteradm init --wait
```
```text
/hub/kubeconfig
      |
      +--> pre-existing external Secret
      |
      +--> communicates with OCM hub
      |
      +--> clusteradm get token

This installs the OCM hub control plane.

The chart then creates the configured `ManagedCluster` resources.
/tmp/managed-kubeconfig
      |
      +--> generated temporarily by join Job
      |
      +--> communicates with local managed cluster
      |
      +--> clusteradm join
```

For example:
`/hub/kubeconfig` is mounted from the Secret configured by:

```yaml
apiVersion: cluster.open-cluster-management.io/v1
kind: ManagedCluster
metadata:
  name: cluster1
spec:
  hubAcceptsClient: true
  leaseDurationSeconds: 60
hubKubeconfigSecretName: "ocm-hub-kubeconfig"
```

The hub-side acceptance CronJob automatically approves registration requests for the clusters configured in `managedClusters`.
`/tmp/managed-kubeconfig` is generated temporarily using the installer ServiceAccount and is used by `clusteradm join` to configure the local Kubernetes cluster.

## OpenZiti Bootstrap

Conceptually:
During the initial Helm installation, the chart automatically creates the OpenZiti identity required by the managed cluster.

```text
Managed cluster
Authenticate to OpenZiti Controller
        |
        v
Registration CSR
Create cluster1-ziti identity
        |
        v
accept-clusters CronJob
Retrieve enrollment JWT
        |
        v
clusteradm accept
Enroll identity
        |
        v
CSR approved
Generate identity.json
        |
        v
Create Kubernetes Secret
ziti-cluster1-identity
        |
        v
ManagedCluster connected
Start ziti-edge-tunnel
```

## OCM Hub API through OpenZiti

The hub chart creates the OpenZiti objects required to expose the Kubernetes API through the OpenZiti overlay.

Conceptually:
The generated identity JSON is stored in the Kubernetes Secret under:

```text
hub-ziti
    |
    | Bind
    v
ocm-hub-api
    |
    +--> intercept.v1
    |
    +--> host.v1
            |
            v
       HUB_IP:6443
persisted-identity
```

The `host.v1` configuration points to the actual Kubernetes API endpoint on the hub.
The `ziti-edge-tunnel` subchart mounts this Secret and starts the OpenZiti tunneler using the enrolled managed-cluster identity.

For example:
## Direct Connectivity and OpenZiti Fallback

Before retrieving the OCM registration token, the join Job extracts the hub API endpoint from:

```text
192.168.5.185:6443
/hub/kubeconfig
```

The `intercept.v1` configuration uses the same hub API address and port.
If direct connectivity works:

The managed chart can later create a cluster-specific Dial policy when direct communication with this endpoint is unavailable.
```text
Managed Cluster
      |
      +-------- direct --------> Hub API
```

No OpenZiti Dial policy is created.

The resulting OpenZiti path is:
If direct connectivity is unavailable:

```text
Managed Cluster
cluster1-ziti
      |
      | Dial
@@ -290,53 +293,57 @@ hub-ziti
      |
      v
Hub Kubernetes API
HUB_IP:6443
```

The hub chart owns the following OpenZiti objects:
The managed chart creates only the cluster-specific Dial policy.

For example:

```text
ocm-hub-api-cluster1-dial
```

The following OpenZiti objects are created and owned by the `ocm-hub` chart:

```text
hub-ziti
ocm-hub-api
ocm-hub-api-intercept
ocm-hub-api-host
ocm-hub-api-bind
```

The managed charts only create their own cluster-specific Dial policies when required.
The managed chart only creates the Dial permission required for its own OpenZiti identity to consume the hub API service.

## Managed Cluster Configuration
## OCM Join Process

Managed clusters are declared in `values.yaml`:
The join Job retrieves the OCM registration information using:

```yaml
managedClusters:
  - cluster1
  - cluster2
```

The list represents the desired OCM managed-cluster state.

To add a cluster:

```yaml
managedClusters:
  - cluster1
  - cluster2
  - cluster3
```bash
clusteradm \
  --kubeconfig /hub/kubeconfig \
  get token
```

Apply the change with:
It then executes:

```bash
helm upgrade ocm-hub . -n ocm-hub
clusteradm join \
  --hub-token <token> \
  --hub-apiserver <hub-api> \
  --wait \
  --cluster-name <cluster-name> \
  --kubeconfig /tmp/managed-kubeconfig \
  --context managed-context
```

The chart creates the missing `ManagedCluster` resources.
`clusteradm join` installs and configures the OCM Klusterlet on the local managed cluster.

To remove a cluster, remove it from the list and run the same Helm upgrade.
The Klusterlet includes:

The chart reconciles the current OCM `ManagedCluster` resources with the configured list.
* `registration-agent`
* `work-agent`

The registration agent creates the registration CSR on the hub. The hub-side acceptance CronJob subsequently approves the registration.

## Installation

@@ -346,109 +353,109 @@ Update Helm dependencies:
helm dependency update
```

Install the hub chart:
Create the namespace:

```bash
helm install ocm-hub . \
  -n ocm-hub \
  --create-namespace \
  --timeout 10m
kubectl create namespace ocm-managed
```

## Verification

Check the OCM hub:
Create the hub kubeconfig Secret:

```bash
kubectl get clustermanager
kubectl create secret generic ocm-hub-kubeconfig \
  -n ocm-managed \
  --from-file=kubeconfig=hub-kubeconfig.yaml
```

Check managed clusters:
Install the chart:

```bash
kubectl get managedclusters
helm install ocm-managed . \
  -n ocm-managed \
  --set clusterName=cluster1 \
  --set hubKubeconfigSecretName=ocm-hub-kubeconfig \
  --timeout 10m
```

Expected after registration:
If the values are already defined in `values.yaml`, the installation can simply be:

```text
NAME       HUB ACCEPTED   MANAGED CLUSTER URLS   JOINED   AVAILABLE
cluster1   true                                  True     True
```bash
helm install ocm-managed . \
  -n ocm-managed \
  --timeout 10m
```

Check registration CSRs:
## Verification

Check the Klusterlet:

```bash
kubectl get csr
kubectl get klusterlet
```

Check the automatic acceptance CronJob:
Check OCM agents:

```bash
kubectl get cronjob -n ocm-hub
kubectl get pods -n open-cluster-management-agent
```

Check the OpenZiti tunneler:

```bash
kubectl get pods -n ocm-hub \
kubectl get pods -n ocm-managed \
  -l app.kubernetes.io/name=ziti-edge-tunnel
```

Check the tunneler logs:

```bash
kubectl logs -n ocm-hub \
kubectl logs -n ocm-managed \
  -l app.kubernetes.io/name=ziti-edge-tunnel
```

Check the tunneler image:

```bash
kubectl get pods -n ocm-hub \
kubectl get pods -n ocm-managed \
  -l app.kubernetes.io/name=ziti-edge-tunnel \
  -o jsonpath='{range .items[*]}{.metadata.name}{" -> "}{.spec.containers[*].image}{"\n"}{end}'
```

## Helm Upgrade Behaviour
## Secret Ownership

The OpenZiti identity is created during the initial installation and reused during normal Helm upgrades.
The chart uses two different Secrets with different ownership:

```text
helm install
ocm-hub-kubeconfig
    |
     +--> Create OpenZiti identity
     +--> Enroll identity
     +--> Create identity Secret
     +--> Start tunneler
     +--> Initialize OCM hub
    +--> created externally
    +--> contains hub kubeconfig
    +--> mounted by join Job
    +--> NOT deleted by Helm cleanup

helm upgrade

ziti-cluster1-identity
    |
     +--> Reuse existing OpenZiti identity
     +--> Reuse existing identity Secret
     +--> Reconcile managedClusters
     +--> Apply configuration changes
    +--> created by OpenZiti bootstrap Job
    +--> contains OpenZiti identity
    +--> mounted by ziti-edge-tunnel
    +--> deleted during Helm cleanup
```

This avoids unnecessary re-enrollment of the OpenZiti identity during normal chart upgrades.

## Uninstall

```bash
helm uninstall ocm-hub -n ocm-hub
helm uninstall ocm-managed -n ocm-managed
```

The cleanup hook removes:

* configured `ManagedCluster` resources;
* related registration CSRs;
* OCM hub resources;
* OCM namespaces and stale resources;
* the `ocm-hub-api` Bind policy;
* the `ocm-hub-api` OpenZiti service;
* the associated `intercept.v1` configuration;
* the associated `host.v1` configuration;
* the `hub-ziti` identity;
* the Klusterlet;
* OCM agent resources and namespaces;
* the cluster-specific OpenZiti Dial policy, if present;
* the managed-cluster OpenZiti identity;
* the generated OpenZiti identity Secret;
* OpenZiti bootstrap RBAC resources.
* OpenZiti bootstrap Role and RoleBinding;
* OpenZiti bootstrap ServiceAccount.

The externally supplied hub kubeconfig Secret is not removed.