# Deploying Eclipse Che on K3s

2024-03-09

A non-exhaustive guide to running Eclipse Che on K3s, with Keycloak handling the logins.

## Backstory

I recently spent an entire weekend deploying [Eclipse Che](https://eclipse.dev/che/) on a [K3s](https://k3s.io/) cluster, using [Keycloak](https://www.keycloak.org/) as the OIDC provider. Although it was a lot of fun, I was surprised by how little documentation and how few tutorials exist for this exact combination. So, to save you some time and frustration, I decided to write down how I did it.

The tricky part is that three different things have to agree with each other for login to work: Keycloak issues the tokens, the Kubernetes API server has to accept them, and Che uses the same provider to sign you in. That means the issuer URL and the client ID have to be exactly the same in all three places. If login doesn't work at the end, that's the first thing I'd check.

One more thing before we start: this is from March 2024. Chart values and Che's configuration tend to change, so double-check everything against the versions you're installing.

## Prerequisites

Before you start, make sure you have the following:

- A K3s cluster up and running, and `kubectl` access to it
- Traefik installed as your ingress controller (K3s ships with it by default)
- DNS records for `keycloak.example.com` and `che.example.com` pointing to your cluster
- TLS certificates for both domains (ideally a wildcard certificate)
- [chectl](https://github.com/che-incubator/chectl) installed on the machine you manage the cluster from

Throughout this guide, I'll use `che` as the Keycloak realm and `eclipse-che` as the client ID. Don't forget to replace the example domains with your own.

## Deploying Keycloak

First things first, let's give Keycloak its own namespace. Save this as `keycloak-namespace.yaml`:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: keycloak
```

And apply it:

```shell
kubectl apply -f keycloak-namespace.yaml
```

Now we're ready to install Keycloak itself. K3s comes with a built-in [Helm controller](https://docs.k3s.io/add-ons/helm), so instead of running Helm ourselves, we can describe the chart as a `HelmChart` resource and let K3s take care of the installation. Save this as `keycloak-chart.yaml`, and be sure to set a proper password for the admin user in the `valuesContent` section:

```yaml
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: keycloak
  namespace: kube-system
spec:
  repo: https://charts.bitnami.com/bitnami
  chart: keycloak
  targetNamespace: keycloak
  valuesContent: |-
    auth:
      adminUser: admin
      adminPassword: REPLACE_WITH_YOUR_ADMIN_PASSWORD
    ingress:
      enabled: false
    proxy: edge
    production: true
```

```shell
kubectl apply -f keycloak-chart.yaml
```

Since we disabled the chart's own ingress, we need to create one ourselves to make Keycloak reachable from outside the cluster. Save this as `keycloak-ingress.yaml`, and replace `keycloak-tls` with the name of the secret that holds your certificate:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: keycloak-ingress
  namespace: keycloak
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - keycloak.example.com
      secretName: keycloak-tls
  rules:
    - host: keycloak.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 80
```

```shell
kubectl apply -f keycloak-ingress.yaml
```

If everything goes smoothly, you should be able to open `https://keycloak.example.com` once the pods are ready, and log in to the admin console with the credentials from `valuesContent`.

## Configuring Keycloak

To use Keycloak as an OIDC provider, we'll create a new realm (think of it as an authentication zone) and a client specifically for Eclipse Che.

In the admin console, create a realm called `che`. Inside that realm, create an OpenID Connect client with the client ID `eclipse-che`. The client needs client authentication enabled, and its redirect URI has to match Che's login callback, which for the example domain is `https://che.example.com/oauth/callback`.

Once the client exists, Keycloak generates a secret for it, which you'll find in the client's Credentials tab. Copy it somewhere, because we'll need it for the Che configuration later.

The issuer URL of the realm is `https://keycloak.example.com/realms/che`. This is the URL both K3s and Che need to know about, and it really has to point to the realm. The Keycloak homepage, or a different realm, won't work.

If you want a second source for the client settings, Che's own docs on [using Keycloak as an external identity provider](https://eclipse.dev/che/docs/stable/install/proc_installing-che-on-openshift-with-keycloak-as-oidc/) describe them too. Their cluster setup steps are for OpenShift, though, so the K3s part follows below.

## Configuring K3s

Now we need to tell K3s to trust the tokens Keycloak issues. Edit (or create) the K3s configuration file at `/etc/rancher/k3s/config.yaml` and add the following lines, merging them with any `kube-apiserver-arg` entries you might already have:

```yaml
kube-apiserver-arg:
  - "oidc-issuer-url=https://keycloak.example.com/realms/che"
  - "oidc-client-id=eclipse-che"
  - "oidc-username-claim=email"
```

The issuer URL tells the API server which provider to trust, the client ID is the audience the tokens have to be issued for, and the username claim tells Kubernetes which field of the token to use as your username. Kubernetes documents all of these in its [OpenID Connect guide](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).

Then restart the K3s server to apply the changes:

```shell
sudo systemctl restart k3s
```

This part of the setup has also been documented by the funkypenguin geek cookbook, both for [Keycloak](https://geek-cookbook.funkypenguin.co.nz/kubernetes/oidc-authentication/keycloak/) and for [K3s](https://geek-cookbook.funkypenguin.co.nz/kubernetes/oidc-authentication/k3s-keycloak/). I recommend following those guides if you're not familiar with Keycloak yet.

## Deploying Eclipse Che

We're almost there! For the actual deployment, I used chectl, Che's own command-line tool, which installs the Che operator for you. We'll need two more files.

`che-cluster.yaml` is a patch for Che's `CheCluster` resource, and it's where we tell Che to use Keycloak. It points to the same realm and client as the K3s configuration and uses `email` as the username claim. Replace `REPLACE_WITH_KEYCLOAK_CLIENT_SECRET` with the client secret you copied earlier:

```yaml
kind: CheCluster
apiVersion: org.eclipse.che/v2
spec:
  networking:
    auth:
      externalIdentityProvider: true
      openShiftoAuth: false
      oAuthClientName: eclipse-che
      oAuthSecret: REPLACE_WITH_KEYCLOAK_CLIENT_SECRET
      identityProviderURL: "https://keycloak.example.com/realms/che"
  components:
    cheServer:
      extraProperties:
        CHE_OIDC_USERNAME__CLAIM: email
```

`che-ingress.yaml` is similar to the Keycloak ingress file, but routes the Che domain to Che's gateway service:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: eclipse-che-ingress
  namespace: eclipse-che
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - che.example.com
      secretName: che-tls
  rules:
    - host: che.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: che-gateway
                port:
                  number: 8080
```

<IngressRoutesFigure />

Make sure the `che-tls` secret exists in the `eclipse-che` namespace. Then deploy Che with the patch, and apply the ingress once all pods are running:

```shell
chectl server:deploy \
  --domain che.example.com \
  --platform k8s \
  --che-operator-cr-patch-yaml che-cluster.yaml \
  --skip-cert-manager

kubectl apply -f che-ingress.yaml
```

The `--skip-cert-manager` flag is there because we already took care of TLS ourselves.

## Checking the login flow

And that's it! Open `https://che.example.com`, and you should get redirected to Keycloak and back to Che after logging in. I'd also create a workspace right away, because a running pod alone doesn't mean authentication works. A workspace is the real test of whether your session can actually do something.

If login fails, compare the issuer URL, client ID and client secret across all three configurations first. And if Keycloak complains about an invalid redirect URI, check the callback URL in the login request against the one you registered on the client.
