kadet-hub logokadet-hub

Matrix Synapse on Kubernetes with Kapitan

Synapse 1.164.2, the Matrix homeserver, with federation and open registration off and its first admin registered by a Job, with Element Web and OpenID Connect as options, rendered by the kadet-hub kubernetes generator 1.14.0 from Synapse’s image. The database is the included recipe postgresql.

What it deploys

Required parameters

applications.matrix-synapse.server_name is the domain in every user ID and room alias, such as example.com in @admin:example.com. It cannot change later: Synapse refuses to start on a database that holds users of another server name.

applications.matrix-synapse.registration_shared_secret lets register_new_matrix_user and the admin API register accounts, admins included, and also signs Synapse’s macaroons. Set it to a long random string, such as one that openssl rand -hex 32 prints.

applications.matrix-synapse.admin.username and applications.matrix-synapse.admin.password are the localpart and the password of the first admin. The Job matrix-synapse-admin registers the account with register_new_matrix_user --exists-ok once Synapse answers, and leaves an existing account as it is. A later change of the password does not reach the account. Change it in a client or through the admin API.

The option oidc needs applications.matrix-synapse.oidc.issuer, the issuer URL of the OpenID Connect provider, such as a Keycloak realm, and applications.matrix-synapse.oidc.client_id and applications.matrix-synapse.oidc.client_secret, Synapse’s client at the provider.

Set the shared secret, the password and the client secret as refs with the backend of your choice. Without one of the parameters the compile stops with Interpolation key '<key>' not found. postgresql requires no parameter.

Include

A target includes the recipe and its options as classes and can override anything the recipe sets:

classes:
  - recipes.matrix-synapse
  - recipes.matrix-synapse.element-web    # Element Web
  - recipes.matrix-synapse.federation     # federation with other servers
  - recipes.matrix-synapse.oidc           # login through OpenID Connect
parameters:
  namespace: matrix-synapse
  kapitan:
    vars:
      target: matrix-synapse
  applications:
    matrix-synapse:
      server_name: example.com
      registration_shared_secret: <ref>
      admin:
        username: admin
        password: <ref>
      oidc:
        issuer: https://keycloak.example.com/realms/example
        client_id: synapse
        client_secret: <ref>

Fetching it

The quickstart goes through these steps on a kind cluster with the uptime-kuma recipe, from installing Kapitan to the running app.

A separate fetch inventory pins the recipe by version and digest. This is fetch/targets/recipes.yml for version 1.0.0:

parameters:
  kapitan:
    dependencies:
      - type: oci
        source: ghcr.io/kadet-hub/recipes/matrix-synapse:1.0.0@sha256:53c575c0f9d62a0b9506c2aa98fb63f35f1f6a00362c4c6a6eb2abdf7e85d885
        output_path: inventory/classes/recipes/matrix-synapse

Version 1.0.0 includes postgresql 1.1.1. The consumer workflow adds the included recipes at the digests the index lists. Kapitan's --fetch reads no index, so for a local compile pull them by digest first:

tmp=$(mktemp -d)
oras pull -o "$tmp" ghcr.io/kadet-hub/recipes/postgresql@sha256:1e8465058c256cc86995dc8d6f1eb2f095df244fadcaf756935599f4d9ead6a9
mkdir -p inventory/classes/recipes/postgresql
tar -xf "$tmp/postgresql.tar" -C inventory/classes/recipes/postgresql --no-same-owner

The first command fetches the recipe into inventory/classes/recipes/matrix-synapse, the second the generators and compiles:

kapitan compile --inventory-path fetch --fetch --output-path .
kapitan compile --fetch

The first command exits with status 1 because the fetch target compiles nothing. Kapitan's --fetch checks neither the digest nor the attestation. This verifies the attestation of the recipe and of the recipes it includes:

gh attestation verify \
  oci://ghcr.io/kadet-hub/recipes/matrix-synapse@sha256:53c575c0f9d62a0b9506c2aa98fb63f35f1f6a00362c4c6a6eb2abdf7e85d885 \
  --owner kadet-hub \
  --cert-identity https://github.com/kadet-hub/registry/.github/workflows/release.yml@refs/heads/main

gh attestation verify \
  oci://ghcr.io/kadet-hub/recipes/postgresql@sha256:1e8465058c256cc86995dc8d6f1eb2f095df244fadcaf756935599f4d9ead6a9 \
  --owner kadet-hub \
  --cert-identity https://github.com/kadet-hub/registry/.github/workflows/release.yml@refs/heads/main

The attested index must also list each digest under its recipe with the pinned version, and not as yanked. Verifying by hand shows how to read it.

Settings

<s> stands for components.matrix-synapse, <w> for components.matrix-synapse-element-web.

Setting Inventory
URL clients reach Synapse at, https://<server_name>/ applications.matrix-synapse.public_baseurl, such as https://matrix.example.com/ when Synapse has a host of its own
Synapse version <s>.image, tag and digest
Element Web version <w>.image, tag and digest
other Synapse settings a key <name>.yaml in <s>.config_maps.config.data with the settings as its value. Synapse reads the files in name order, and a later file replaces a top-level key of an earlier one, so keep each top-level key in one file.
further keys of Element Web’s config.json <w>.config_maps.config.data.config\.json.value
size of the claim data, 10Gi <s>.volume_claims.data.spec.resources.requests.storage
resources <s>.resources, <w>.resources
exposure a route or Ingress of the target to the Service matrix-synapse on port 8008, and to matrix-synapse-element-web on port 80

Synapse reads its configuration only when it starts, so after a change restart it with kubectl rollout restart statefulset/matrix-synapse. Synapse reads the database password from postgresql-cluster-app as PGPASSWORD and connects with sslmode=require, which encrypts the connection without verifying the server’s certificate. Synapse requires the collation C for its database, which CloudNativePG gives a new Cluster by default.

The Job matrix-synapse-admin waits in an init container until Synapse answers through its Service, and goes 10 minutes after it finished. Applying a new version of the recipe creates it again, and it changes nothing for an existing admin.

Any list set in the target is appended to the recipe’s list. To replace one, set the key to ~ in a class included after the recipe and the new list in the target.

Proof

check matrix-synapse --level deploys with tests/target.yml, on kind for each Kubernetes minor CI tests, with cnpg installed first. An image has no Kubernetes objects to compare with, so the recipe is proven by deploying it (RCP-44). tests/target.yml includes the options element-web and oidc, runs one PostgreSQL instance and sets public_baseurl to the Service matrix-synapse, so that the test reaches the homeserver at the URL Element Web names. Its OpenID Connect issuer does not resolve.

tests/smoke checks that the pods pass Pod Security restricted. Through the Service, Synapse reports the version of the image tag, answers the federation API and the server keys with 404, and a registration with 403. The admin of the Job logs in, creates a second user, a room with that user invited, an upload and a message. The second user joins and reads the message and the upload, again after the test deleted the pod matrix-synapse-0, with the same signing key on the claim. The login flows name the provider of the option oidc. Element Web answers through its Service, and the homeserver answers at the URL its config.json names. In Chrome, driven through ChromeDriver, Element Web’s login page names example.com and the provider Single sign-on, which it read from the homeserver at that URL. GitHub’s runners bring Chrome and ChromeDriver. Elsewhere set CHROMEDRIVER to a ChromeDriver of the installed Chrome’s version. A login through an OpenID Connect provider is not tested.

check deploys one target, so the option federation is proven by hand on kind. Two targets with the option federated, and with the option off on one of them the join got 404 (RCP-362 in docs/specs/recipes.md).

Versions

The recipe's files are in kadet-hub/recipes. All recipes