From ec6bacb14c9e36b9ccee26fdd7cc386496ae8da1 Mon Sep 17 00:00:00 2001 From: Mathieu Grzybek Date: Mon, 25 May 2026 20:34:46 +0200 Subject: [PATCH 1/3] feat(ring0): migrate middlewares to FluxCD Operator with OCI releases --- ring0/README.md | 79 +++++-- .../pki/debian-pki-cloud-init.sh | 25 +++ ring0/flux/apps/03-idp/helmrelease.yaml | 53 +++++ ring0/flux/apps/03-idp/kustomization.yaml | 4 + ring0/flux/apps/04-cmdb/helmrelease.yaml | 63 ++++++ ring0/flux/apps/04-cmdb/kustomization.yaml | 4 + .../apps/04-eso/cluster-secret-store.yaml | 24 ++ ring0/flux/apps/04-eso/helmrelease.yaml | 20 ++ ring0/flux/apps/04-eso/kustomization.yaml | 7 + .../apps/05-bmaas/kamaji/helmrelease.yaml | 21 ++ .../apps/05-bmaas/kamaji/kustomization.yaml | 4 + ring0/flux/apps/05-bmaas/kustomization.yaml | 6 + .../apps/05-bmaas/tinkerbell/helmrelease.yaml | 82 +++++++ .../05-bmaas/tinkerbell/kustomization.yaml | 4 + ring0/flux/apps/05-bmaas/zot/helmrelease.yaml | 23 ++ .../flux/apps/05-bmaas/zot/kustomization.yaml | 4 + ring0/flux/apps/kustomization.yaml | 7 + ring0/flux/clusters/management/apps.yaml | 16 ++ .../management/flux-system/flux-instance.yaml | 27 +++ .../clusters/management/infrastructure.yaml | 14 ++ .../clusters/management/kustomization.yaml | 6 + .../00-namespaces/kustomization.yaml | 4 + .../00-namespaces/namespaces.yaml | 48 ++++ .../01-cert-manager/cluster-issuer.yaml | 19 ++ .../01-cert-manager/helmrelease.yaml | 23 ++ .../01-cert-manager/kustomization.yaml | 7 + .../01-cilium/gateway-api-crds.yaml | 15 ++ .../infrastructure/01-cilium/helmrelease.yaml | 79 +++++++ .../01-cilium/kustomization.yaml | 5 + .../01-cilium/l2-announcement.yaml | 20 ++ .../02-cnpg-operator/helmrelease.yaml | 18 ++ .../02-cnpg-operator/kustomization.yaml | 4 + .../helmrelease.yaml | 18 ++ .../kustomization.yaml | 4 + .../infrastructure/02-pg-cluster/cluster.yaml | 37 ++++ .../02-pg-cluster/kustomization.yaml | 4 + .../02-tailscale-operator/helmrelease.yaml | 28 +++ .../02-tailscale-operator/kustomization.yaml | 4 + ring0/flux/infrastructure/kustomization.yaml | 11 + .../repositories/helm-authentik.yaml | 8 + .../repositories/helm-cilium.yaml | 8 + .../repositories/helm-clastix.yaml | 8 + .../repositories/helm-cnpg.yaml | 8 + .../repositories/helm-external-secrets.yaml | 8 + .../repositories/helm-jetstack.yaml | 8 + .../repositories/helm-rancher.yaml | 8 + .../repositories/helm-tailscale.yaml | 8 + .../infrastructure/repositories/helm-zot.yaml | 8 + .../repositories/kustomization.yaml | 14 ++ .../repositories/oci-netbox.yaml | 9 + .../repositories/oci-tinkerbell.yaml | 9 + ring0/scripts/README.md | 49 ++++- ring0/scripts/deploy-bmaas.sh | 23 +- ring0/scripts/deploy-cmdb.sh | 13 +- ring0/scripts/deploy-flux.sh | 208 ++++++++++++++++++ ring0/scripts/deploy-idp.sh | 8 +- ring0/scripts/deploy-pki.sh | 29 ++- ring0/scripts/management/install-bmaas.sh | 109 --------- .../management/install-platform-management.sh | 133 ----------- ring0/taskfile.yml | 81 +++++-- 60 files changed, 1279 insertions(+), 319 deletions(-) create mode 100644 ring0/flux/apps/03-idp/helmrelease.yaml create mode 100644 ring0/flux/apps/03-idp/kustomization.yaml create mode 100644 ring0/flux/apps/04-cmdb/helmrelease.yaml create mode 100644 ring0/flux/apps/04-cmdb/kustomization.yaml create mode 100644 ring0/flux/apps/04-eso/cluster-secret-store.yaml create mode 100644 ring0/flux/apps/04-eso/helmrelease.yaml create mode 100644 ring0/flux/apps/04-eso/kustomization.yaml create mode 100644 ring0/flux/apps/05-bmaas/kamaji/helmrelease.yaml create mode 100644 ring0/flux/apps/05-bmaas/kamaji/kustomization.yaml create mode 100644 ring0/flux/apps/05-bmaas/kustomization.yaml create mode 100644 ring0/flux/apps/05-bmaas/tinkerbell/helmrelease.yaml create mode 100644 ring0/flux/apps/05-bmaas/tinkerbell/kustomization.yaml create mode 100644 ring0/flux/apps/05-bmaas/zot/helmrelease.yaml create mode 100644 ring0/flux/apps/05-bmaas/zot/kustomization.yaml create mode 100644 ring0/flux/apps/kustomization.yaml create mode 100644 ring0/flux/clusters/management/apps.yaml create mode 100644 ring0/flux/clusters/management/flux-system/flux-instance.yaml create mode 100644 ring0/flux/clusters/management/infrastructure.yaml create mode 100644 ring0/flux/clusters/management/kustomization.yaml create mode 100644 ring0/flux/infrastructure/00-namespaces/kustomization.yaml create mode 100644 ring0/flux/infrastructure/00-namespaces/namespaces.yaml create mode 100644 ring0/flux/infrastructure/01-cert-manager/cluster-issuer.yaml create mode 100644 ring0/flux/infrastructure/01-cert-manager/helmrelease.yaml create mode 100644 ring0/flux/infrastructure/01-cert-manager/kustomization.yaml create mode 100644 ring0/flux/infrastructure/01-cilium/gateway-api-crds.yaml create mode 100644 ring0/flux/infrastructure/01-cilium/helmrelease.yaml create mode 100644 ring0/flux/infrastructure/01-cilium/kustomization.yaml create mode 100644 ring0/flux/infrastructure/01-cilium/l2-announcement.yaml create mode 100644 ring0/flux/infrastructure/02-cnpg-operator/helmrelease.yaml create mode 100644 ring0/flux/infrastructure/02-cnpg-operator/kustomization.yaml create mode 100644 ring0/flux/infrastructure/02-local-path-provisioner/helmrelease.yaml create mode 100644 ring0/flux/infrastructure/02-local-path-provisioner/kustomization.yaml create mode 100644 ring0/flux/infrastructure/02-pg-cluster/cluster.yaml create mode 100644 ring0/flux/infrastructure/02-pg-cluster/kustomization.yaml create mode 100644 ring0/flux/infrastructure/02-tailscale-operator/helmrelease.yaml create mode 100644 ring0/flux/infrastructure/02-tailscale-operator/kustomization.yaml create mode 100644 ring0/flux/infrastructure/kustomization.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-authentik.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-cilium.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-clastix.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-cnpg.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-external-secrets.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-jetstack.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-rancher.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-tailscale.yaml create mode 100644 ring0/flux/infrastructure/repositories/helm-zot.yaml create mode 100644 ring0/flux/infrastructure/repositories/kustomization.yaml create mode 100644 ring0/flux/infrastructure/repositories/oci-netbox.yaml create mode 100644 ring0/flux/infrastructure/repositories/oci-tinkerbell.yaml create mode 100644 ring0/scripts/deploy-flux.sh diff --git a/ring0/README.md b/ring0/README.md index 05c5a9e..dd04ad4 100644 --- a/ring0/README.md +++ b/ring0/README.md @@ -230,22 +230,67 @@ export BMAAS_NAMESPACE=bmaas-system task management ``` -## Installing the middlewares +## Bootstrapping Flux (GitOps) -### Installing the IDP service +After `task management`, bootstrap FluxCD Operator so it takes over all platform workloads. +The script creates the pre-requisite Secrets and the `cluster-config` ConfigMap, then applies the `FluxInstance`. ```shell -task idp +# Required variables (add to your variables.sh / environment) +export TS_OPERATOR_CLIENT_ID=xxxxxx +export TS_OPERATOR_CLIENT_SECRET=xxxxxx +export PKI_ENDPOINT=https://pki. +export PKI_ORG="My Cloud" +export DNS_IP=192.168.3.7 +export HOOKOS_IP=192.168.3.6 +export REGISTRY_IP=192.168.3.4 +export TINKERBELL_IP=192.168.3.5 +export ARTIFACTS_FILE_SERVER=http:/// +export DHCP_BIND_INTERFACE=eth0 +export BOOTSTRAP_ENDPOINT=http:///assets/tinkerbell +export ANNOUNCEMENTS_IFACE=eth1 # interface on management node facing the services VLAN +export GHCR_TOKEN= + +task flux ``` -> [!WARNING] -> Installing Authentik can be quite long because of the database initialization. +From this point Flux manages the following components from the OCI artifact (`ghcr.io/mgrzybek/micro-cloud`): + +| Flux path | Component | Namespace | +| --- | --- | --- | +| `infrastructure/01-cilium` | Cilium CNI | kube-system | +| `infrastructure/01-cert-manager` | cert-manager | cert-manager | +| `infrastructure/02-cnpg-operator` | CloudNative PG operator | cnpg-system | +| `infrastructure/02-pg-cluster` | PostgreSQL cluster | platform-management | +| `infrastructure/02-tailscale-operator` | Tailscale Operator | tailscale | +| `apps/03-idp` | Authentik (IDP) | platform-management | +| `apps/04-cmdb` | Netbox (CMDB) | platform-management | +| `apps/04-eso` | External Secrets Operator | external-secrets | +| `apps/05-bmaas/zot` | Zot OCI registry | tinkerbell-system | +| `apps/05-bmaas/kamaji` | Kamaji | kamaji-system | +| `apps/05-bmaas/tinkerbell` | Tinkerbell | tinkerbell-system | + +Monitor reconciliation with: + +```shell +flux get all -A +``` + +## Post-Flux setup + +### Exposing the IDP service + +Once Flux has deployed Authentik, expose it on the tailnet: + +```shell +task idp +``` Now you are ready to populate your directory as needed. Please note that Netbox uses two groups by default: `staff` and `superusers`. You have to add some users to these groups to be able to manage Netbox. -If you want to use Authentik's APIΒ to provision resources, you should create a token using the admin account at [https://idp.your-tailscale-suffix/if/admin/#/core/tokens).](https://idp/if/admin/#/core/tokens). +If you want to use Authentik's API to provision resources, you should create a token using the admin account at [https://idp.your-tailscale-suffix/if/admin/#/core/tokens).](https://idp/if/admin/#/core/tokens). -### Configuring the Netbox provider +### Exposing the CMDB service The official documentation on how to integrate the SSO mechanism between Authentik and Netbox is [described here](https://integrations.goauthentik.io/documentation/netbox/). @@ -262,15 +307,6 @@ task cmdb > [!WARNING] > Installing Netbox can be quite long because of the database initialization. -### Installing External Secrets Operator - -Deploys ESO and creates a `ClusterSecretStore` backed by OpenBao (KV v2, AppRole auth). -The AppRole credentials are generated automatically during `task intermediate-fullchain`. - -```shell -task eso -``` - ### Configuring Tinkerbell Some pre-configuration is needed to make CoreDNS use Netbox as an IPAM. You must create a `coredns` service account able to read IP addresses from the IPAM section. @@ -285,6 +321,17 @@ export DNS_IP=192.168.3.7 task bmaas ``` +## Releasing a new version + +Push a semver tag to trigger the GitHub Actions workflow that publishes the Flux manifests as an OCI artifact: + +```bash +git tag v1.2.3 +git push origin v1.2.3 +``` + +Flux picks up the new tag automatically (or pin a specific version in `flux/clusters/management/flux-system/flux-instance.yaml`). + ## Day-2: Adding a physical worker node A physical machine can be added to the management cluster as a worker node via PXE boot through Matchbox. diff --git a/ring0/core-services/pki/debian-pki-cloud-init.sh b/ring0/core-services/pki/debian-pki-cloud-init.sh index ece8f01..a4d23a8 100644 --- a/ring0/core-services/pki/debian-pki-cloud-init.sh +++ b/ring0/core-services/pki/debian-pki-cloud-init.sh @@ -8,6 +8,16 @@ if [[ -z "$SUFFIX" ]]; then exit 1 fi +if [[ -z "$SERVER_ADDR" ]]; then + echo "SERVER_ADDR must be present in /etc/cloud.sh" + exit 1 +fi + +if [[ -z "$SERVER_CIDR" ]]; then + echo "SERVER_CIDR must be present in /etc/cloud.sh" + exit 1 +fi + function main() { set -e @@ -38,6 +48,21 @@ function prepare() { apt -y install jq unzip wget export HOME=/root export pki=/var/lib/pki/files + + echo "#####################" + echo "πŸ‘· Configuring the netboot iface" + + cat < + values: + authentik: + error_reporting: + enabled: true + server: + ingress: + enabled: true + hosts: [] + postgresql: + enabled: false + redis: + enabled: true + global: + env: + - name: AUTHENTIK_POSTGRESQL__HOST + value: tooling-rw + - name: AUTHENTIK_POSTGRESQL__NAME + value: authentik + - name: AUTHENTIK_POSTGRESQL__USER + value: app + - name: AUTHENTIK_POSTGRESQL__PASSWORD + valueFrom: + secretKeyRef: + name: tooling-app + key: password diff --git a/ring0/flux/apps/03-idp/kustomization.yaml b/ring0/flux/apps/03-idp/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/apps/03-idp/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/apps/04-cmdb/helmrelease.yaml b/ring0/flux/apps/04-cmdb/helmrelease.yaml new file mode 100644 index 0000000..296b8f2 --- /dev/null +++ b/ring0/flux/apps/04-cmdb/helmrelease.yaml @@ -0,0 +1,63 @@ +# cmdb-netbox-remote-auth secret must exist before reconciliation. +# task flux creates it by rendering netbox-remote-auth.py.j2 with the Authentik OAuth credentials. +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: cmdb + namespace: platform-management +spec: + interval: 1h + chart: + spec: + chart: netbox + sourceRef: + kind: HelmRepository + name: netbox-community + namespace: flux-system + values: + defaultLanguage: fr-fr + postgresql: + enabled: false + externalDatabase: + existingSecretName: tooling-app + existingSecretKey: password + host: tooling-rw + port: 5432 + username: app + database: netbox + valkey: + architecture: standalone + extraConfig: + - secret: + secretName: cmdb-netbox-remote-auth + items: + - netbox-remote-auth.py + optional: false + extraDeploy: + - "apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: cmdb-netbox-sso-pipeline-roles\n namespace: platform-management\ndata:\n sso_pipeline_roles.py: |\n from netbox.authentication import Group\n class AuthFailed(Exception):\n pass\n def add_groups(response, user, backend, *args, **kwargs):\n try:\n groups = response['groups']\n except KeyError:\n pass\n for group in groups:\n group, created = Group.objects.get_or_create(name=group)\n user.groups.add(group)\n def remove_groups(response, user, backend, *args, **kwargs):\n try:\n groups = response['groups']\n except KeyError:\n user.groups.clear()\n pass\n user_groups = [item.name for item in user.groups.all()]\n delete_groups = list(set(user_groups) - set(groups))\n for delete_group in delete_groups:\n group = Group.objects.get(name=delete_group)\n user.groups.remove(group)\n def set_roles(response, user, backend, *args, **kwargs):\n user.is_superuser = False\n user.is_staff = False\n try:\n groups = response['groups']\n except KeyError:\n user.save()\n pass\n user.is_superuser = True if 'superusers' in groups else False\n user.is_staff = True if 'staff' in groups else False\n user.save()\n" + extraVolumes: + - name: remote-auth + secret: + secretName: cmdb-netbox-remote-auth + - name: sso-pipeline-roles + configMap: + name: cmdb-netbox-sso-pipeline-roles + defaultMode: 0755 + - name: internal-ca-chain + configMap: + name: internal-ca-chain + extraVolumeMounts: + - name: remote-auth + mountPath: /etc/netbox/config/extra.py + subPath: netbox-remote-auth.py + readOnly: true + - name: sso-pipeline-roles + mountPath: /opt/netbox/netbox/netbox/custom_pipeline.py + subPath: sso_pipeline_roles.py + readOnly: true + - name: internal-ca-chain + mountPath: /usr/share/ca-certificates/ + readOnly: true + extraEnvs: + - name: REQUESTS_CA_BUNDLE + value: /usr/share/ca-certificates/internal-ca.crt diff --git a/ring0/flux/apps/04-cmdb/kustomization.yaml b/ring0/flux/apps/04-cmdb/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/apps/04-cmdb/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/apps/04-eso/cluster-secret-store.yaml b/ring0/flux/apps/04-eso/cluster-secret-store.yaml new file mode 100644 index 0000000..a478fd8 --- /dev/null +++ b/ring0/flux/apps/04-eso/cluster-secret-store.yaml @@ -0,0 +1,24 @@ +# openbao-eso-approle secret must exist before this reconciles. +# task flux creates it from dist/openbao-eso-role-id and dist/openbao-eso-secret-id. +# +# pki_endpoint and openbao_ca_bundle are read from cluster-config ConfigMap by task flux +# and written into this manifest via a post-render patch OR set directly at bootstrap. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao +spec: + provider: + vault: + server: "" + path: "secret" + version: "v2" + caBundle: "" + auth: + appRole: + path: approle + roleId: "" + secretRef: + namespace: external-secrets + name: openbao-eso-approle + key: secretId diff --git a/ring0/flux/apps/04-eso/helmrelease.yaml b/ring0/flux/apps/04-eso/helmrelease.yaml new file mode 100644 index 0000000..cb02e2f --- /dev/null +++ b/ring0/flux/apps/04-eso/helmrelease.yaml @@ -0,0 +1,20 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: external-secrets + namespace: external-secrets +spec: + interval: 1h + chart: + spec: + chart: external-secrets + sourceRef: + kind: HelmRepository + name: external-secrets + namespace: flux-system + install: + crds: CreateReplace + upgrade: + crds: CreateReplace + values: + installCRDs: true diff --git a/ring0/flux/apps/04-eso/kustomization.yaml b/ring0/flux/apps/04-eso/kustomization.yaml new file mode 100644 index 0000000..040daf1 --- /dev/null +++ b/ring0/flux/apps/04-eso/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml + # cluster-secret-store.yaml is NOT managed by Flux: the ClusterSecretStore + # references the OpenBao URL, CA bundle and AppRole credentials. + # task flux creates it imperatively from dist/ artifacts. diff --git a/ring0/flux/apps/05-bmaas/kamaji/helmrelease.yaml b/ring0/flux/apps/05-bmaas/kamaji/helmrelease.yaml new file mode 100644 index 0000000..45ce784 --- /dev/null +++ b/ring0/flux/apps/05-bmaas/kamaji/helmrelease.yaml @@ -0,0 +1,21 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: kamaji + namespace: kamaji-system +spec: + interval: 1h + chart: + spec: + chart: kamaji + sourceRef: + kind: HelmRepository + name: clastix + namespace: flux-system + install: + crds: CreateReplace + upgrade: + crds: CreateReplace + values: + etcd: + deploy: true diff --git a/ring0/flux/apps/05-bmaas/kamaji/kustomization.yaml b/ring0/flux/apps/05-bmaas/kamaji/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/apps/05-bmaas/kamaji/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/apps/05-bmaas/kustomization.yaml b/ring0/flux/apps/05-bmaas/kustomization.yaml new file mode 100644 index 0000000..4f80d51 --- /dev/null +++ b/ring0/flux/apps/05-bmaas/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - zot + - kamaji + - tinkerbell diff --git a/ring0/flux/apps/05-bmaas/tinkerbell/helmrelease.yaml b/ring0/flux/apps/05-bmaas/tinkerbell/helmrelease.yaml new file mode 100644 index 0000000..da2f541 --- /dev/null +++ b/ring0/flux/apps/05-bmaas/tinkerbell/helmrelease.yaml @@ -0,0 +1,82 @@ +# cluster-config ConfigMap keys used here (created by task flux): +# dhcp_bind_interface, bootstrap_endpoint, hookos_ip, tinkerbell_ip, +# artifacts_file_server, registry_ip +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: tinkerbell + namespace: tinkerbell-system +spec: + interval: 1h + chart: + spec: + chart: tinkerbell + version: "v0.19.1" + sourceRef: + kind: HelmRepository + name: tinkerbell + namespace: flux-system + valuesFrom: + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: dhcp_bind_interface + targetPath: deployment.envs.smee.dhcpBindInterface + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: bootstrap_endpoint + targetPath: optional.hookos.downloadURL + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: hookos_ip + targetPath: optional.hookos.service.annotations["io\.cilium/lb-ipam-ips"] + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: tinkerbell_ip + targetPath: service.annotations["io\.cilium/lb-ipam-ips"] + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: tinkerbell_ip + targetPath: publicIP + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: artifacts_file_server + targetPath: artifactsFileServer + values: + deployment: + agentImage: registry.ring0:5000/tinkerbell/tink-agent + hostNetwork: true + image: ghcr.io/tinkerbell/tinkerbell + envs: + globals: + enableRufioController: false + enableSecondstar: false + smee: + ipxeHttpScriptExtraKernelArgs: + - console=ttyS0 + - insecure_registries=registry.ring0:5000 + tinkController: + enableLeaderElection: false + tinkServer: + autoDiscoveryAutoEnrollmentEnabled: true + autoDiscoveryEnabled: true + optional: + hookos: + hostNetwork: false + arch: x86_64 + kernelVersion: both + service: + labels: + ring0/services: "true" + lbClass: + kubevip: + enabled: false + service: + labels: + ring0/services: "true" + lbClass: diff --git a/ring0/flux/apps/05-bmaas/tinkerbell/kustomization.yaml b/ring0/flux/apps/05-bmaas/tinkerbell/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/apps/05-bmaas/tinkerbell/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/apps/05-bmaas/zot/helmrelease.yaml b/ring0/flux/apps/05-bmaas/zot/helmrelease.yaml new file mode 100644 index 0000000..eeaa7e5 --- /dev/null +++ b/ring0/flux/apps/05-bmaas/zot/helmrelease.yaml @@ -0,0 +1,23 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: zot + namespace: tinkerbell-system +spec: + interval: 1h + chart: + spec: + chart: zot + sourceRef: + kind: HelmRepository + name: project-zot + namespace: flux-system + values: + service: + type: ClusterIP + port: 5000 + persistence: true + pvc: + create: true + strategy: + type: Recreate diff --git a/ring0/flux/apps/05-bmaas/zot/kustomization.yaml b/ring0/flux/apps/05-bmaas/zot/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/apps/05-bmaas/zot/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/apps/kustomization.yaml b/ring0/flux/apps/kustomization.yaml new file mode 100644 index 0000000..a6f20c5 --- /dev/null +++ b/ring0/flux/apps/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - 04-eso + - 03-idp + - 04-cmdb + - 05-bmaas diff --git a/ring0/flux/clusters/management/apps.yaml b/ring0/flux/clusters/management/apps.yaml new file mode 100644 index 0000000..2aebbfb --- /dev/null +++ b/ring0/flux/clusters/management/apps.yaml @@ -0,0 +1,16 @@ +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: apps + namespace: flux-system +spec: + interval: 10m + path: ./ring0/flux/apps + prune: true + sourceRef: + kind: OCIRepository + name: flux + dependsOn: + - name: infrastructure + wait: true + timeout: 30m diff --git a/ring0/flux/clusters/management/flux-system/flux-instance.yaml b/ring0/flux/clusters/management/flux-system/flux-instance.yaml new file mode 100644 index 0000000..b284a35 --- /dev/null +++ b/ring0/flux/clusters/management/flux-system/flux-instance.yaml @@ -0,0 +1,27 @@ +apiVersion: fluxcd.controlplane.io/v1 +kind: FluxInstance +metadata: + name: flux + namespace: flux-system + annotations: + fluxcd.controlplane.io/reconcileEvery: "1h" +spec: + distribution: + version: "2.x" + registry: "ghcr.io/fluxcd" + components: + - source-controller + - kustomize-controller + - helm-controller + - notification-controller + storage: + class: local-path + size: 10Gi + cluster: + type: kubernetes + sync: + kind: OCIRepository + url: oci://ghcr.io/mgrzybek/micro-cloud + ref: latest + path: ./ring0/flux/clusters/management + pullSecret: ghcr-auth diff --git a/ring0/flux/clusters/management/infrastructure.yaml b/ring0/flux/clusters/management/infrastructure.yaml new file mode 100644 index 0000000..cfac5d1 --- /dev/null +++ b/ring0/flux/clusters/management/infrastructure.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: infrastructure + namespace: flux-system +spec: + interval: 10m + path: ./ring0/flux/infrastructure + prune: true + sourceRef: + kind: OCIRepository + name: flux + wait: true + timeout: 30m diff --git a/ring0/flux/clusters/management/kustomization.yaml b/ring0/flux/clusters/management/kustomization.yaml new file mode 100644 index 0000000..ec94ade --- /dev/null +++ b/ring0/flux/clusters/management/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - flux-system/flux-instance.yaml + - infrastructure.yaml + - apps.yaml diff --git a/ring0/flux/infrastructure/00-namespaces/kustomization.yaml b/ring0/flux/infrastructure/00-namespaces/kustomization.yaml new file mode 100644 index 0000000..a1eee58 --- /dev/null +++ b/ring0/flux/infrastructure/00-namespaces/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - namespaces.yaml diff --git a/ring0/flux/infrastructure/00-namespaces/namespaces.yaml b/ring0/flux/infrastructure/00-namespaces/namespaces.yaml new file mode 100644 index 0000000..2b55fe7 --- /dev/null +++ b/ring0/flux/infrastructure/00-namespaces/namespaces.yaml @@ -0,0 +1,48 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: platform-management + name: platform-management +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: kamaji-system + name: kamaji-system +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: tinkerbell-system + name: tinkerbell-system +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: cert-manager + name: cert-manager +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: cnpg-system + name: cnpg-system +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: external-secrets + name: external-secrets +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubernetes.io/metadata.name: tailscale + name: tailscale diff --git a/ring0/flux/infrastructure/01-cert-manager/cluster-issuer.yaml b/ring0/flux/infrastructure/01-cert-manager/cluster-issuer.yaml new file mode 100644 index 0000000..6661fa5 --- /dev/null +++ b/ring0/flux/infrastructure/01-cert-manager/cluster-issuer.yaml @@ -0,0 +1,19 @@ +# openbao-approle secret must be created before this reconciles. +# task flux creates it from dist/openbao-approle-role-id and dist/openbao-approle-secret-id. +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: openbao-internal-ca +spec: + vault: + path: pki_int/sign/microcloud-host + # server and caBundle are patched by task flux from cluster-config + server: "" + caBundle: "" + auth: + appRole: + path: approle + roleId: "" + secretRef: + name: openbao-approle + key: secretId diff --git a/ring0/flux/infrastructure/01-cert-manager/helmrelease.yaml b/ring0/flux/infrastructure/01-cert-manager/helmrelease.yaml new file mode 100644 index 0000000..12cc77b --- /dev/null +++ b/ring0/flux/infrastructure/01-cert-manager/helmrelease.yaml @@ -0,0 +1,23 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: cert-manager + namespace: cert-manager +spec: + interval: 1h + chart: + spec: + chart: cert-manager + sourceRef: + kind: HelmRepository + name: jetstack + namespace: flux-system + install: + crds: CreateReplace + upgrade: + crds: CreateReplace + values: + crds: + enabled: true + extraArgs: + - --enable-gateway-api diff --git a/ring0/flux/infrastructure/01-cert-manager/kustomization.yaml b/ring0/flux/infrastructure/01-cert-manager/kustomization.yaml new file mode 100644 index 0000000..b967d32 --- /dev/null +++ b/ring0/flux/infrastructure/01-cert-manager/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml + # cluster-issuer.yaml is NOT managed by Flux: the ClusterIssuer references + # the OpenBao URL and CA bundle which are environment-specific secrets. + # task flux creates it imperatively from dist/ artifacts. diff --git a/ring0/flux/infrastructure/01-cilium/gateway-api-crds.yaml b/ring0/flux/infrastructure/01-cilium/gateway-api-crds.yaml new file mode 100644 index 0000000..118293b --- /dev/null +++ b/ring0/flux/infrastructure/01-cilium/gateway-api-crds.yaml @@ -0,0 +1,15 @@ +# Gateway API CRDs required by Cilium β€” version must match install-platform-management.sh +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: gateway-api-crds + namespace: flux-system +spec: + interval: 24h + path: ./ring0/flux/infrastructure/01-cilium/gateway-api + prune: false + sourceRef: + kind: OCIRepository + name: flux + wait: true + timeout: 5m diff --git a/ring0/flux/infrastructure/01-cilium/helmrelease.yaml b/ring0/flux/infrastructure/01-cilium/helmrelease.yaml new file mode 100644 index 0000000..c0cbe1c --- /dev/null +++ b/ring0/flux/infrastructure/01-cilium/helmrelease.yaml @@ -0,0 +1,79 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: cilium + namespace: kube-system +spec: + interval: 1h + chart: + spec: + chart: cilium + sourceRef: + kind: HelmRepository + name: cilium + namespace: flux-system + install: + crds: CreateReplace + upgrade: + crds: CreateReplace + valuesFrom: + - kind: ConfigMap + name: cluster-config + namespace: flux-system + valuesKey: announcements_iface + targetPath: l2announcements.interface + values: + cgroup: + autoMount: + enabled: false + hostRoot: /sys/fs/cgroup + gatewayAPI: + enabled: true + hostNetwork: + enabled: false + envoy: + securityContext: + capabilities: + keepCapNetBindService: true + securityContext: + capabilities: + ciliumAgent: + - CHOWN + - KILL + - NET_ADMIN + - NET_RAW + - IPC_LOCK + - SYS_ADMIN + - SYS_RESOURCE + - DAC_OVERRIDE + - FOWNER + - SETGID + - SETUID + cleanCiliumState: + - NET_ADMIN + - SYS_ADMIN + - SYS_RESOURCE + ipam: + mode: kubernetes + hubble: + relay: + enabled: true + ui: + enabled: true + service: + annotations: + tailscale.com/expose: "true" + tailscale.com/hostname: hubble + k8sServiceHost: localhost + kubeProxyReplacement: true + externalIPs: + enabled: true + l7Proxy: true + l2announcements: + enabled: true + leaseDuration: 3s + leaseRenewDeadline: 1s + leaseRetryPeriod: 500ms + k8sClientRateLimit: + qps: 5 + burst: 10 diff --git a/ring0/flux/infrastructure/01-cilium/kustomization.yaml b/ring0/flux/infrastructure/01-cilium/kustomization.yaml new file mode 100644 index 0000000..d073b0d --- /dev/null +++ b/ring0/flux/infrastructure/01-cilium/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml + - l2-announcement.yaml diff --git a/ring0/flux/infrastructure/01-cilium/l2-announcement.yaml b/ring0/flux/infrastructure/01-cilium/l2-announcement.yaml new file mode 100644 index 0000000..3fa98c1 --- /dev/null +++ b/ring0/flux/infrastructure/01-cilium/l2-announcement.yaml @@ -0,0 +1,20 @@ +# L2 announcement pool β€” IPs come from the cluster-config ConfigMap created at bootstrap +apiVersion: "cilium.io/v2alpha1" +kind: CiliumLoadBalancerIPPool +metadata: + name: management-services +spec: + blocks: [] + # Populated via patch from cluster-config at bootstrap (task flux) +--- +apiVersion: "cilium.io/v2alpha1" +kind: CiliumL2AnnouncementPolicy +metadata: + name: management-services +spec: + nodeSelector: + matchLabels: + ring0/services: "true" + interfaces: [] + # Interface populated via patch from cluster-config at bootstrap (task flux) + loadBalancerIPs: true diff --git a/ring0/flux/infrastructure/02-cnpg-operator/helmrelease.yaml b/ring0/flux/infrastructure/02-cnpg-operator/helmrelease.yaml new file mode 100644 index 0000000..95358bf --- /dev/null +++ b/ring0/flux/infrastructure/02-cnpg-operator/helmrelease.yaml @@ -0,0 +1,18 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: cnpg + namespace: cnpg-system +spec: + interval: 1h + chart: + spec: + chart: cloudnative-pg + sourceRef: + kind: HelmRepository + name: cnpg + namespace: flux-system + install: + crds: CreateReplace + upgrade: + crds: CreateReplace diff --git a/ring0/flux/infrastructure/02-cnpg-operator/kustomization.yaml b/ring0/flux/infrastructure/02-cnpg-operator/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/infrastructure/02-cnpg-operator/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/infrastructure/02-local-path-provisioner/helmrelease.yaml b/ring0/flux/infrastructure/02-local-path-provisioner/helmrelease.yaml new file mode 100644 index 0000000..b5a972f --- /dev/null +++ b/ring0/flux/infrastructure/02-local-path-provisioner/helmrelease.yaml @@ -0,0 +1,18 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: local-path-provisioner + namespace: kube-system +spec: + interval: 1h + chart: + spec: + chart: local-path-provisioner + version: "0.0.32" + sourceRef: + kind: HelmRepository + name: rancher + namespace: flux-system + values: + storageClass: + defaultClass: true diff --git a/ring0/flux/infrastructure/02-local-path-provisioner/kustomization.yaml b/ring0/flux/infrastructure/02-local-path-provisioner/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/infrastructure/02-local-path-provisioner/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/infrastructure/02-pg-cluster/cluster.yaml b/ring0/flux/infrastructure/02-pg-cluster/cluster.yaml new file mode 100644 index 0000000..488af34 --- /dev/null +++ b/ring0/flux/infrastructure/02-pg-cluster/cluster.yaml @@ -0,0 +1,37 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: tooling + namespace: platform-management +spec: + instances: 1 + storage: + size: 5Gi + managed: + roles: + - name: app + createdb: true + createrole: true + login: true +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: netbox + namespace: platform-management +spec: + name: netbox + owner: app + cluster: + name: tooling +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Database +metadata: + name: authentik + namespace: platform-management +spec: + name: authentik + owner: app + cluster: + name: tooling diff --git a/ring0/flux/infrastructure/02-pg-cluster/kustomization.yaml b/ring0/flux/infrastructure/02-pg-cluster/kustomization.yaml new file mode 100644 index 0000000..993fbde --- /dev/null +++ b/ring0/flux/infrastructure/02-pg-cluster/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - cluster.yaml diff --git a/ring0/flux/infrastructure/02-tailscale-operator/helmrelease.yaml b/ring0/flux/infrastructure/02-tailscale-operator/helmrelease.yaml new file mode 100644 index 0000000..8bd04b3 --- /dev/null +++ b/ring0/flux/infrastructure/02-tailscale-operator/helmrelease.yaml @@ -0,0 +1,28 @@ +# oauth.clientId and oauth.clientSecret are injected via the tailscale-operator-oauth Secret, +# created by task flux from TS_OPERATOR_CLIENT_ID / TS_OPERATOR_CLIENT_SECRET env vars. +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: tailscale-operator + namespace: tailscale +spec: + interval: 1h + chart: + spec: + chart: tailscale-operator + sourceRef: + kind: HelmRepository + name: tailscale + namespace: flux-system + valuesFrom: + - kind: Secret + name: tailscale-operator-oauth + valuesKey: clientId + targetPath: oauth.clientId + - kind: Secret + name: tailscale-operator-oauth + valuesKey: clientSecret + targetPath: oauth.clientSecret + values: + ingressClass: + enabled: false diff --git a/ring0/flux/infrastructure/02-tailscale-operator/kustomization.yaml b/ring0/flux/infrastructure/02-tailscale-operator/kustomization.yaml new file mode 100644 index 0000000..4fd939d --- /dev/null +++ b/ring0/flux/infrastructure/02-tailscale-operator/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helmrelease.yaml diff --git a/ring0/flux/infrastructure/kustomization.yaml b/ring0/flux/infrastructure/kustomization.yaml new file mode 100644 index 0000000..9f7c2b6 --- /dev/null +++ b/ring0/flux/infrastructure/kustomization.yaml @@ -0,0 +1,11 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - repositories + - 00-namespaces + - 01-cilium + - 01-cert-manager + - 02-local-path-provisioner + - 02-cnpg-operator + - 02-pg-cluster + - 02-tailscale-operator diff --git a/ring0/flux/infrastructure/repositories/helm-authentik.yaml b/ring0/flux/infrastructure/repositories/helm-authentik.yaml new file mode 100644 index 0000000..36ea80f --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-authentik.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: authentik + namespace: flux-system +spec: + interval: 24h + url: https://charts.goauthentik.io diff --git a/ring0/flux/infrastructure/repositories/helm-cilium.yaml b/ring0/flux/infrastructure/repositories/helm-cilium.yaml new file mode 100644 index 0000000..712edc5 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-cilium.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: cilium + namespace: flux-system +spec: + interval: 24h + url: https://helm.cilium.io/ diff --git a/ring0/flux/infrastructure/repositories/helm-clastix.yaml b/ring0/flux/infrastructure/repositories/helm-clastix.yaml new file mode 100644 index 0000000..6a743d8 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-clastix.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: clastix + namespace: flux-system +spec: + interval: 24h + url: https://clastix.github.io/charts diff --git a/ring0/flux/infrastructure/repositories/helm-cnpg.yaml b/ring0/flux/infrastructure/repositories/helm-cnpg.yaml new file mode 100644 index 0000000..c3965b1 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-cnpg.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: cnpg + namespace: flux-system +spec: + interval: 24h + url: https://cloudnative-pg.github.io/charts diff --git a/ring0/flux/infrastructure/repositories/helm-external-secrets.yaml b/ring0/flux/infrastructure/repositories/helm-external-secrets.yaml new file mode 100644 index 0000000..4faa188 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-external-secrets.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: external-secrets + namespace: flux-system +spec: + interval: 24h + url: https://charts.external-secrets.io diff --git a/ring0/flux/infrastructure/repositories/helm-jetstack.yaml b/ring0/flux/infrastructure/repositories/helm-jetstack.yaml new file mode 100644 index 0000000..0f5b730 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-jetstack.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: jetstack + namespace: flux-system +spec: + interval: 24h + url: https://charts.jetstack.io diff --git a/ring0/flux/infrastructure/repositories/helm-rancher.yaml b/ring0/flux/infrastructure/repositories/helm-rancher.yaml new file mode 100644 index 0000000..ab39607 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-rancher.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: rancher + namespace: flux-system +spec: + interval: 24h + url: https://charts.rancher.io diff --git a/ring0/flux/infrastructure/repositories/helm-tailscale.yaml b/ring0/flux/infrastructure/repositories/helm-tailscale.yaml new file mode 100644 index 0000000..ed14aad --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-tailscale.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: tailscale + namespace: flux-system +spec: + interval: 24h + url: https://pkgs.tailscale.com/helmcharts diff --git a/ring0/flux/infrastructure/repositories/helm-zot.yaml b/ring0/flux/infrastructure/repositories/helm-zot.yaml new file mode 100644 index 0000000..0e3f9e4 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/helm-zot.yaml @@ -0,0 +1,8 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: project-zot + namespace: flux-system +spec: + interval: 24h + url: https://zotregistry.dev/helm-charts diff --git a/ring0/flux/infrastructure/repositories/kustomization.yaml b/ring0/flux/infrastructure/repositories/kustomization.yaml new file mode 100644 index 0000000..a0f1105 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - helm-cilium.yaml + - helm-jetstack.yaml + - helm-cnpg.yaml + - helm-tailscale.yaml + - helm-rancher.yaml + - helm-external-secrets.yaml + - helm-authentik.yaml + - helm-zot.yaml + - helm-clastix.yaml + - oci-netbox.yaml + - oci-tinkerbell.yaml diff --git a/ring0/flux/infrastructure/repositories/oci-netbox.yaml b/ring0/flux/infrastructure/repositories/oci-netbox.yaml new file mode 100644 index 0000000..be009ce --- /dev/null +++ b/ring0/flux/infrastructure/repositories/oci-netbox.yaml @@ -0,0 +1,9 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: netbox-community + namespace: flux-system +spec: + interval: 24h + type: oci + url: oci://ghcr.io/netbox-community/netbox-chart diff --git a/ring0/flux/infrastructure/repositories/oci-tinkerbell.yaml b/ring0/flux/infrastructure/repositories/oci-tinkerbell.yaml new file mode 100644 index 0000000..bb10f56 --- /dev/null +++ b/ring0/flux/infrastructure/repositories/oci-tinkerbell.yaml @@ -0,0 +1,9 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: tinkerbell + namespace: flux-system +spec: + interval: 24h + type: oci + url: oci://ghcr.io/tinkerbell/charts diff --git a/ring0/scripts/README.md b/ring0/scripts/README.md index 082e2f7..0fb85c2 100644 --- a/ring0/scripts/README.md +++ b/ring0/scripts/README.md @@ -1,14 +1,39 @@ # Scripts -- [common.sh](common.sh): bash fonctions used by the other scripts. -- [deploy-bmaas.sh](./deploy-bmaas.sh): deploys the bare metal as a service software. -- [deploy-eso.sh](./deploy-eso.sh): deploys External Secrets Operator and creates the ClusterSecretStore backed by OpenBao. -- [deploy-bootstrap.sh](./deploy-bootstrap.sh): creates the bootstrap instance and configure its services. -- [deploy-cmdb.sh](./deploy-cmdb.sh): deploys the CMDB software. -- [deploy-forge.sh](./deploy-forge.sh): creates the forge instance and installes some building tools. -- [deploy-idp.sh](./deploy-idp.sh): deploys the identity provider. -- [deploy-management.sh](./deploy-management.sh): creates the management instance and configures the cluster. -- [management/add-worker.sh](./management/add-worker.sh): adds a physical worker node to the management cluster via Matchbox PXE boot (use `task add-worker`). Requires `GITOPS_ROOT` pointing to the gitops repository and a per-machine patch file at `$GITOPS_ROOT/ring0/workers/$WORKER_HOSTNAME/patch.yaml`. -- [deploy-netboot-testing-vm.sh](./deploy-netboot-testing-vm.sh): creates a dummy machine that you be enrolled by the BMaaS. -- [deploy-pki.sh](./deploy-pki.sh): creates the PKI instance and configures the certificates and services. -- [init-headnode.sh](./init-headnode.sh): configures the headnode in an interactive way. +## Bootstrap (one-shot, imperative) + +These scripts run sequentially to provision the physical infrastructure. +They are not managed by Flux. + +- [deploy-pki.sh](deploy-pki.sh): creates the PKI Incus instance, initialises cfssl and OpenBao, generates the intermediate CA and AppRole credentials consumed by cert-manager and ESO. +- [deploy-bootstrap.sh](deploy-bootstrap.sh): creates the bootstrap Incus instance and configures Matchbox for PXE booting. +- [deploy-forge.sh](deploy-forge.sh): creates the forge Incus instance used to build custom OCI images (CoreDNS with Netbox plugin, HookOS). +- [deploy-management.sh](deploy-management.sh): creates the management Talos instance, bootstraps the Kubernetes cluster, and installs the core components (Cilium, cert-manager, CNPG, Tailscale Operator, local-path-provisioner, PostgreSQL cluster). +- [deploy-flux.sh](deploy-flux.sh): bootstraps FluxCD Operator; creates the pre-requisite Secrets and the `cluster-config` ConfigMap; applies the `FluxInstance`. Run after `deploy-management.sh`. From this point on, Flux manages all platform workloads. + +## Post-Flux setup (API gateways) + +These scripts create the Tailscale `Service` and TLS `Certificate` that expose each +application on the tailnet. They run after Flux has deployed the underlying Helm +chart and Tailscale has assigned a tailnet IP to the service. + +- [deploy-idp.sh](deploy-idp.sh): creates the Tailscale API gateway for Authentik (`idp.`). Authentik itself is deployed by Flux. +- [deploy-cmdb.sh](deploy-cmdb.sh): creates the Tailscale API gateway for Netbox (`cmdb.`). Netbox itself is deployed by Flux. + +## BMaaS operations + +Steps that remain outside Flux because they involve artifact builds and binary +operations on the Incus instances. + +- [deploy-bmaas.sh](deploy-bmaas.sh): builds and syncs HookOS to the bootstrap server; populates the Zot registry with Tinkerbell action images; runs `clusterctl init`. Zot, Tinkerbell, and Kamaji are deployed by Flux. + +## Day-2 / worker nodes + +- [management/add-worker.sh](management/add-worker.sh): adds a physical worker node to the management cluster via Matchbox PXE boot (use `task add-worker`). Requires `GITOPS_ROOT` pointing to the gitops repository and a per-machine patch file at `$GITOPS_ROOT/ring0/workers/$WORKER_HOSTNAME/patch.yaml`. +- [deploy-netboot-testing-vm.sh](deploy-netboot-testing-vm.sh): creates a dummy VM to validate the PXE/BMaaS enrolment flow. +- [init-headnode.sh](init-headnode.sh): configures the headnode interactively (first run only). +- [upgrade-management.sh](upgrade-management.sh): upgrades the Talos management cluster. + +## Shared library + +- [common.sh](common.sh): bash functions used by all other scripts (`print_milestone`, `print_step`, `print_check`, …). diff --git a/ring0/scripts/deploy-bmaas.sh b/ring0/scripts/deploy-bmaas.sh index 18eb88e..9fb6422 100644 --- a/ring0/scripts/deploy-bmaas.sh +++ b/ring0/scripts/deploy-bmaas.sh @@ -32,28 +32,21 @@ fi ################################################################################ # Starting the tasks +# Zot, Tinkerbell, and Kamaji are installed by Flux (HelmReleases in apps/05-bmaas/). +# This script handles the steps that remain outside Flux: +# - Build and sync HookOS artifacts to the bootstrap server +# - Populate the Zot registry with Tinkerbell action images +# - Install Cluster API (clusterctl init) if ! is_hook_synced; then build_hook copy_hook_to_bootstrap fi -create_announcement_configuration -install_registry_api_gateway - if ! helm list -n "$BMAAS_NAMESPACE" -o json | jq -e '.[] | select(.name=="zot")' >/dev/null 2>&1; then - install_zot - populate_zot -else - print_check "Zot is already installed" -fi - -if ! helm list -n "$BMAAS_NAMESPACE" -o json | jq -e '.[] | select(.name=="tinkerbell")' >/dev/null 2>&1; then - install_tinkerbell -else - print_check "Tinkerbell is already installed" + echo "Zot not yet ready β€” waiting for Flux to deploy it before populating the registry" + kubectl wait --for=condition=Ready --timeout=600s -n "$BMAAS_NAMESPACE" pod/zot-0 fi - -install_kamaji +populate_zot install_cluster_api diff --git a/ring0/scripts/deploy-cmdb.sh b/ring0/scripts/deploy-cmdb.sh index 6063ef6..0a30a6a 100644 --- a/ring0/scripts/deploy-cmdb.sh +++ b/ring0/scripts/deploy-cmdb.sh @@ -13,15 +13,8 @@ source "$RING0_ROOT/scripts/management/install-platform-management.sh" ################################################################################ # Starting the tasks +# Netbox is installed by Flux (HelmRelease apps/04-cmdb). +# The cmdb-netbox-remote-auth secret is created by deploy-flux.sh. +# This script only creates the Tailscale API gateway needed to expose cmdb.. install_cmdb_api_gateway - -if ! kubectl get secret -n platform-management cmdb-netbox-remote-auth >/dev/null 2>&1; then - create_remote_netbox_auth_secret -fi - -if ! helm list -n platform-management -o json | jq -e '.[] | select(.name=="cmdb" and .status=="deployed")' >/dev/null 2>&1; then - install_netbox -else - print_check "Netbox has already been deployed. Nothing to do." -fi diff --git a/ring0/scripts/deploy-flux.sh b/ring0/scripts/deploy-flux.sh new file mode 100644 index 0000000..056c037 --- /dev/null +++ b/ring0/scripts/deploy-flux.sh @@ -0,0 +1,208 @@ +#! /usr/bin/env bash + +set -euo pipefail + +################################################################################ +# External libraries +# shellcheck source=/dev/null +source "$RING0_ROOT/scripts/common.sh" + +################################################################################ +# Prerequisites checks + +for var in \ + TS_SUFFIX \ + PKI_ENDPOINT \ + PKI_ORG \ + TS_OPERATOR_CLIENT_ID \ + TS_OPERATOR_CLIENT_SECRET \ + DNS_IP \ + HOOKOS_IP \ + REGISTRY_IP \ + TINKERBELL_IP \ + ARTIFACTS_FILE_SERVER \ + DHCP_BIND_INTERFACE \ + BOOTSTRAP_ENDPOINT \ + ANNOUNCEMENTS_IFACE \ + GHCR_TOKEN; do + if [[ -z "${!var:-}" ]]; then + echo "ERROR: $var must be defined" + exit 1 + fi +done + +for file in \ + "$RING0_ROOT/dist/bundle.crt" \ + "$RING0_ROOT/dist/openbao-approle-role-id" \ + "$RING0_ROOT/dist/openbao-approle-secret-id" \ + "$RING0_ROOT/dist/openbao-eso-role-id" \ + "$RING0_ROOT/dist/openbao-eso-secret-id"; do + if [[ ! -f "$file" ]]; then + echo "ERROR: $file missing β€” run task intermediate-fullchain first" + exit 1 + fi +done + +print_milestone "Deploying FluxCD Operator and bootstrapping GitOps for ring0" + +################################################################################ +# Step 1 β€” Create ghcr.io pull secret so Flux can pull the OCI artifact + +print_step "Creating ghcr-auth pull secret in flux-system" +kubectl create namespace flux-system --dry-run=client -o yaml | kubectl apply -f - +if ! kubectl get secret -n flux-system ghcr-auth >/dev/null 2>&1; then + kubectl create secret docker-registry ghcr-auth \ + --namespace flux-system \ + --docker-server=ghcr.io \ + --docker-username="${GITHUB_ACTOR:-mgrzybek}" \ + --docker-password="$GHCR_TOKEN" +fi +print_check "ghcr-auth pull secret created" + +################################################################################ +# Step 2 β€” Create cluster-config ConfigMap (replaces Jinja2 variables) + +print_step "Creating cluster-config ConfigMap in flux-system" +openbao_ca_bundle="$(base64 <"$RING0_ROOT/dist/bundle.crt" | tr -d '\n')" +kubectl create configmap cluster-config \ + --namespace flux-system \ + --from-literal="announcements_iface=$ANNOUNCEMENTS_IFACE" \ + --from-literal="ts_suffix=$TS_SUFFIX" \ + --from-literal="pki_endpoint=$PKI_ENDPOINT" \ + --from-literal="pki_org=$PKI_ORG" \ + --from-literal="openbao_ca_bundle=$openbao_ca_bundle" \ + --from-literal="dns_ip=$DNS_IP" \ + --from-literal="hookos_ip=$HOOKOS_IP" \ + --from-literal="registry_ip=$REGISTRY_IP" \ + --from-literal="tinkerbell_ip=$TINKERBELL_IP" \ + --from-literal="artifacts_file_server=$ARTIFACTS_FILE_SERVER" \ + --from-literal="dhcp_bind_interface=$DHCP_BIND_INTERFACE" \ + --from-literal="bootstrap_endpoint=$BOOTSTRAP_ENDPOINT" \ + --from-literal="idp_hostname=idp.$TS_SUFFIX" \ + --dry-run=client -o yaml | kubectl apply -f - +print_check "cluster-config ConfigMap created" + +################################################################################ +# Step 3 β€” Create per-component secrets + +print_step "Creating Tailscale operator OAuth secret" +kubectl create namespace tailscale --dry-run=client -o yaml | kubectl apply -f - +if ! kubectl get secret -n tailscale tailscale-operator-oauth >/dev/null 2>&1; then + kubectl create secret generic tailscale-operator-oauth \ + --namespace tailscale \ + --from-literal="clientId=$TS_OPERATOR_CLIENT_ID" \ + --from-literal="clientSecret=$TS_OPERATOR_CLIENT_SECRET" +fi +print_check "tailscale-operator-oauth secret created" + +print_step "Creating cert-manager OpenBao AppRole secret" +kubectl create namespace cert-manager --dry-run=client -o yaml | kubectl apply -f - +approle_role_id="$(cat "$RING0_ROOT/dist/openbao-approle-role-id")" +approle_secret_id="$(cat "$RING0_ROOT/dist/openbao-approle-secret-id")" +if ! kubectl get secret -n cert-manager openbao-approle >/dev/null 2>&1; then + kubectl create secret generic openbao-approle \ + --namespace cert-manager \ + --from-literal="secretId=$approle_secret_id" +fi +if ! kubectl get configmap -n cert-manager internal-ca-chain >/dev/null 2>&1; then + kubectl create configmap internal-ca-chain \ + --namespace cert-manager \ + --from-file="key=$RING0_ROOT/dist/bundle.crt" +fi +print_check "cert-manager secrets created" + +print_step "Creating ESO OpenBao AppRole secret" +kubectl create namespace external-secrets --dry-run=client -o yaml | kubectl apply -f - +eso_role_id="$(cat "$RING0_ROOT/dist/openbao-eso-role-id")" +eso_secret_id="$(cat "$RING0_ROOT/dist/openbao-eso-secret-id")" +if ! kubectl get secret -n external-secrets openbao-eso-approle >/dev/null 2>&1; then + kubectl create secret generic openbao-eso-approle \ + --namespace external-secrets \ + --from-literal="secretId=$eso_secret_id" +fi +print_check "ESO secrets created" + +print_step "Creating Authentik secret_key" +kubectl create namespace platform-management --dry-run=client -o yaml | kubectl apply -f - +if [[ ! -f "$RING0_ROOT/dist/authentik-secret-key" ]]; then + openssl rand -base64 50 | tr -d '\n' >"$RING0_ROOT/dist/authentik-secret-key" +fi +if ! kubectl get secret -n platform-management authentik-secret >/dev/null 2>&1; then + kubectl create secret generic authentik-secret \ + --namespace platform-management \ + --from-literal="secret_key=$(cat "$RING0_ROOT/dist/authentik-secret-key")" +fi +print_check "authentik-secret created" + +################################################################################ +# Step 4 β€” Create ClusterIssuer and ClusterSecretStore (outside Flux management) + +print_step "Creating cert-manager ClusterIssuer" +kubectl apply -f - </dev/null 2>&1 || true +if ! helm list -n flux-system -o json | jq -e '.[] | select(.name=="flux-operator" and .status=="deployed")' >/dev/null 2>&1; then + helm install flux-operator \ + oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator \ + --namespace flux-system \ + --create-namespace \ + --wait +fi +print_check "FluxCD Operator installed" + +################################################################################ +# Step 6 β€” Apply FluxInstance + +print_step "Applying FluxInstance" +kubectl apply -f "$RING0_ROOT/flux/clusters/management/flux-system/flux-instance.yaml" +print_check "FluxInstance applied β€” Flux will now reconcile ring0 from OCI" + +print_check "Bootstrap complete. Monitor with: flux get all -A" diff --git a/ring0/scripts/deploy-idp.sh b/ring0/scripts/deploy-idp.sh index 8d03c4a..c8d3965 100644 --- a/ring0/scripts/deploy-idp.sh +++ b/ring0/scripts/deploy-idp.sh @@ -13,11 +13,7 @@ source "$RING0_ROOT/scripts/management/install-platform-management.sh" ################################################################################ # Starting the tasks +# Authentik is installed by Flux (HelmRelease apps/03-idp). +# This script only creates the Tailscale API gateway needed to expose idp.. install_idp_api_gateway - -if ! helm list -n platform-management -o json | jq -e '.[] | select(.name=="idp" and .status=="deployed")' >/dev/null 2>&1; then - install_authentik -else - print_check "Authentik has already been deployed. Nothing to do." -fi diff --git a/ring0/scripts/deploy-pki.sh b/ring0/scripts/deploy-pki.sh index a2b6304..9b9ea09 100644 --- a/ring0/scripts/deploy-pki.sh +++ b/ring0/scripts/deploy-pki.sh @@ -14,6 +14,21 @@ source "$RING0_ROOT/scripts/common.sh" ################################################################################ # Testing variables +if [[ -z "${BRIDGE_BOOTSTRAP_NAME:-}" ]]; then + echo "BRIDGE_BOOTSTRAP_NAME must be defined" + exit 1 +fi + +if [[ -z "${PKI_BOOTSTRAP_SERVER_ADDR:-}" ]]; then + echo "PKI_BOOTSTRAP_SERVER_ADDR must be defined" + exit 1 +fi + +if [[ -z "${PKI_BOOTSTRAP_SERVER_CIDR:-}" ]]; then + echo "PKI_BOOTSTRAP_SERVER_CIDR must be defined" + exit 1 +fi + if [[ -z "${PKI_COUNTRY:-}" ]]; then echo "PKI_COUNTRY must be defined" exit 1 @@ -68,15 +83,23 @@ function prepare() { echo "Error getting Tailscale's SUFFIX" exit 1 fi - + cat <&2 - return 1 - fi - - # Specify the Tinkerbell Helm chart version, here we use the latest release. - local tinkerbell_chart_version=v0.19.1 - - local instance_bootstrap_ipaddr - instance_bootstrap_ipaddr="$(incus list | awk '/bootstrap/ && /eth1/ {print $6}')" - - # Creating the helm values from template - jinja2 --strict \ - -D "dhcp_bind_interface=$management_services_interface" \ - -D "registry_ip=$REGISTRY_IP" \ - -D "bootstrap_endpoint=http://$instance_bootstrap_ipaddr:8080/assets/tinkerbell" \ - -D "hookos_ip=$HOOKOS_IP" \ - -D "tinkerbell_ip=$TINKERBELL_IP" \ - -D "artifacts_file_server=http://$HOOKOS_IP:7173" \ - "$MANIFESTS_PATH/05-tinkerbell/values.yaml.j2" \ - -o "$RING0_ROOT/dist/tinkerbell-values.yaml" - - create_namespace - - helm install tinkerbell oci://ghcr.io/tinkerbell/charts/tinkerbell \ - --version "$tinkerbell_chart_version" \ - --namespace "$BMAAS_NAMESPACE" \ - --set "trustedProxies={${trusted_proxies}}" \ - --values "$RING0_ROOT/dist/tinkerbell-values.yaml" - kubectl label -n "$BMAAS_NAMESPACE" service hookos ring0/services="true" - - # TODO: publish bundle.crt - ls "$RING0_ROOT/dist/bundle.crt" - - print_check "Checking the deployment" - if kubectl -n "$BMAAS_NAMESPACE" wait --timeout=600s --for=condition=Available deployment/hookos; then - echo "hookos: OK" - fi - if kubectl -n "$BMAAS_NAMESPACE" wait --for=condition=Available deployment/tinkerbell; then - echo "tinkerbell: OK" - fi - echo -} - -function install_zot() { - print_milestone "Installing zot registry" - - create_namespace - - print_step "Installing the helm chart" - if ! helm repo list | grep -qw zot; then - helm repo add project-zot http://zotregistry.dev/helm-charts - helm repo update - fi - helm install zot project-zot/zot --namespace "$BMAAS_NAMESPACE" --values "$MANIFESTS_PATH/05-zot/values.yaml" - - install_registry_api_gateway - - print_check "Checking the deployment" - if kubectl -n "$BMAAS_NAMESPACE" wait --for=condition=Ready --timeout=600s pod/zot-0; then - echo "zot: OK" - fi - echo -} - -function create_namespace() { - # Creating privileged namespace - if ! kubectl get ns "$BMAAS_NAMESPACE" >/dev/null 2>&1; then - kubectl create ns "$BMAAS_NAMESPACE" - kubectl annotate ns "$BMAAS_NAMESPACE" pod-security.kubernetes.io/enforce=privileged - fi -} - function install_registry_api_gateway() { print_milestone "Installing the api gateway used by the registry" @@ -253,32 +170,6 @@ function populate_zot() { done } -function create_announcement_configuration() { - print_milestone "Create Cilium L2 announcement" - - local management_services_interface - management_services_interface="$(talosctl --talosconfig "$RING0_ROOT/dist/talosconfig" -n management -e management get addresses | grep "$INSTANCE_MANAGEMENT_SERVICES_IPADDR_CIDR" | awk '{print $4}' | tail -n1 | awk -F/ '{print $1}')" - - jinja2 --strict \ - -D "dns_ip=$DNS_IP" \ - -D "hookos_ip=$HOOKOS_IP" \ - -D "registry_ip=$REGISTRY_IP" \ - -D "tinkerbell_ip=$TINKERBELL_IP" \ - -D "announcement_interface=$management_services_interface" \ - "$MANIFESTS_PATH/01-cilium/l2-announcement.yaml.j2" \ - -o "$RING0_ROOT/dist/l2-announcement.yaml" - kubectl apply -f "$RING0_ROOT/dist/l2-announcement.yaml" -} - -function install_kamaji() { - print_milestone "Installing kamaji" - - helm upgrade --install kamaji-etcd clastix/kamaji-etcd --namespace kamaji-system --set replicas=1 --set datastore.name=microcloud - - kubectl apply --server-side --force-conflicts -f "$MANIFESTS_PATH/05-kamaji/" - kubectl wait -n kamaji-system --for=condition=Available deployment/kamaji --timeout=600s -} - function install_cluster_api() { print_milestone "Installing cluster api" diff --git a/ring0/scripts/management/install-platform-management.sh b/ring0/scripts/management/install-platform-management.sh index f36ebaf..91ef226 100644 --- a/ring0/scripts/management/install-platform-management.sh +++ b/ring0/scripts/management/install-platform-management.sh @@ -91,55 +91,6 @@ ISSUER print_check "ClusterIssuer openbao-internal-ca created" } -function install_external_secrets() { - print_milestone "Installing External Secrets Operator" - - local eso_role_id eso_secret_id openbao_ca_bundle - - eso_role_id="$(cat "$RING0_ROOT/dist/openbao-eso-role-id")" - eso_secret_id="$(cat "$RING0_ROOT/dist/openbao-eso-secret-id")" - openbao_ca_bundle="$(base64 <"$RING0_ROOT/dist/bundle.crt" | tr -d '\n')" - - helm repo add external-secrets https://charts.external-secrets.io - helm repo update - - if ! helm list -n external-secrets -o json | jq -e '.[] | select(.name=="external-secrets" and .status=="deployed")' >/dev/null 2>&1; then - helm install external-secrets external-secrets/external-secrets \ - --create-namespace --namespace external-secrets \ - --set installCRDs=true - fi - - if ! kubectl get secret -n external-secrets openbao-eso-approle >/dev/null 2>&1; then - kubectl create secret generic openbao-eso-approle \ - --namespace=external-secrets \ - --from-literal="secretId=$eso_secret_id" - fi - - kubectl apply -f - </dev/null 2>&1; then - echo "cmdb-netbox-remote-auth secret not found. Did you run create_remote_netbox_auth_secret before?" - return 1 - else - echo "cmdb-netbox-remote-auth secret found. Nothing to do." - fi - print_check "SSO credentials have been added" - - print_step "Installing the internal ca bundle" - if ! kubectl get configmap -n platform-management internal-ca-chain >/dev/null 2>&1; then - kubectl create configmap internal-ca-chain \ - --namespace=platform-management \ - --from-file="internal-ca.crt=$RING0_ROOT/dist/bundle.crt" - else - echo "internal-ca-chain found. Nothing to do." - fi - print_check "The internal CA bundle has been added" - - print_step "Installing the helm chart" - helm upgrade --install cmdb oci://ghcr.io/netbox-community/netbox-chart/netbox --wait \ - --namespace platform-management \ - --values "$MANIFESTS_PATH/04-cmdb/netbox-values.yaml" \ - --timeout=15m - print_check "Netbox has been installed" - - kubectl wait -n platform-management --for=condition=Available deployment/cmdb-netbox - kubectl wait -n platform-management --for=condition=Available deployment/cmdb-netbox-worker - print_check "Netbox is ready" -} diff --git a/ring0/taskfile.yml b/ring0/taskfile.yml index 66dcc6f..7d137dc 100644 --- a/ring0/taskfile.yml +++ b/ring0/taskfile.yml @@ -9,6 +9,25 @@ tasks: ## intermediate-fullchain: desc: Deploy the PKI and creates the root CA + preconditions: + - sh: tailscale status >/dev/null 2>&1 + msg: Tailscale is not connected β€” run tailscale up first + - sh: test -n "${BRIDGE_BOOTSTRAP_NAME:-}" + msg: BRIDGE_BOOTSTRAP_NAME must be defined + - sh: test -n "${PKI_BOOTSTRAP_SERVER_ADDR:-}" + msg: PKI_BOOTSTRAP_SERVER_ADDR must be defined + - sh: test -n "${PKI_BOOTSTRAP_SERVER_CIDR:-}" + msg: PKI_BOOTSTRAP_SERVER_CIDR must be defined + - sh: test -n "${PKI_COUNTRY:-}" + msg: PKI_COUNTRY must be defined + - sh: test -n "${PKI_LOCATION:-}" + msg: PKI_LOCATION must be defined + - sh: test -n "${PKI_ORG:-}" + msg: PKI_ORG must be defined + - sh: test -n "${PKI_ORG_UNIT:-}" + msg: PKI_ORG_UNIT must be defined + - sh: test -n "${PKI_STATE:-}" + msg: PKI_STATE must be defined cmd: bash ./scripts/deploy-pki.sh generates: [dist/intermediate-fullchain.pem] @@ -30,29 +49,61 @@ tasks: - You should read https://tailscale.com/kb/1236/kubernetes-operator. Is it OK? cmd: RING0_ROOT=$PWD bash ./scripts/deploy-management.sh + flux: + desc: Bootstrap FluxCD Operator and apply the FluxInstance (run after task management) + preconditions: + - sh: test -f dist/bundle.crt + msg: "dist/bundle.crt missing β€” run task intermediate-fullchain first" + - sh: test -f dist/openbao-approle-role-id + msg: "dist/openbao-approle-role-id missing β€” run task intermediate-fullchain first" + - sh: test -f dist/openbao-approle-secret-id + msg: "dist/openbao-approle-secret-id missing β€” run task intermediate-fullchain first" + - sh: test -f dist/openbao-eso-role-id + msg: "dist/openbao-eso-role-id missing β€” run task intermediate-fullchain first" + - sh: test -f dist/openbao-eso-secret-id + msg: "dist/openbao-eso-secret-id missing β€” run task intermediate-fullchain first" + - sh: test -n "${TS_OPERATOR_CLIENT_ID:-}" + msg: TS_OPERATOR_CLIENT_ID must be defined + - sh: test -n "${TS_OPERATOR_CLIENT_SECRET:-}" + msg: TS_OPERATOR_CLIENT_SECRET must be defined + - sh: test -n "${PKI_ENDPOINT:-}" + msg: PKI_ENDPOINT must be defined + - sh: test -n "${PKI_ORG:-}" + msg: PKI_ORG must be defined + - sh: test -n "${DNS_IP:-}" + msg: DNS_IP must be defined + - sh: test -n "${HOOKOS_IP:-}" + msg: HOOKOS_IP must be defined + - sh: test -n "${REGISTRY_IP:-}" + msg: REGISTRY_IP must be defined + - sh: test -n "${TINKERBELL_IP:-}" + msg: TINKERBELL_IP must be defined + - sh: test -n "${ARTIFACTS_FILE_SERVER:-}" + msg: ARTIFACTS_FILE_SERVER must be defined + - sh: test -n "${DHCP_BIND_INTERFACE:-}" + msg: DHCP_BIND_INTERFACE must be defined + - sh: test -n "${BOOTSTRAP_ENDPOINT:-}" + msg: BOOTSTRAP_ENDPOINT must be defined + - sh: test -n "${ANNOUNCEMENTS_IFACE:-}" + msg: ANNOUNCEMENTS_IFACE must be defined + - sh: test -n "${GHCR_TOKEN:-}" + msg: GHCR_TOKEN must be defined (GitHub PAT or classic token with read:packages) + cmd: >- + RING0_ROOT=$PWD + TS_SUFFIX={{.ts_suffix}} + bash ./scripts/deploy-flux.sh + idp: - desc: Deploy the IDP using Helm + desc: Deploy the IDP Tailscale API gateway (Authentik itself is deployed by Flux) prompt: You should have a look at https://idp.{{.ts_suffix}}/if/flow/initial-setup/ for post-deployment configuration. Right? cmd: bash ./scripts/deploy-idp.sh cmdb: - desc: Deploy the CMDB using Helm + desc: Deploy the CMDB Tailscale API gateway (Netbox itself is deployed by Flux) cmd: bash ./scripts/deploy-cmdb.sh - eso: - desc: Deploy External Secrets Operator with OpenBao integration - preconditions: - - sh: test -f dist/openbao-eso-role-id - msg: "dist/openbao-eso-role-id missing β€” run task intermediate-fullchain first" - - sh: test -f dist/openbao-eso-secret-id - msg: "dist/openbao-eso-secret-id missing β€” run task intermediate-fullchain first" - - sh: test -f dist/bundle.crt - msg: "dist/bundle.crt missing β€” run task intermediate-fullchain first" - cmds: - - bash ./scripts/deploy-eso.sh - bmaas: - desc: Deploy the BMaaS and the OCI registry using helm + desc: Build and sync HookOS, populate the OCI registry, deploy CAPI (Zot/Tinkerbell/Kamaji are deployed by Flux) cmds: - bash ./scripts/deploy-bmaas.sh - task: install-coredns From 24a7e0b4419d6f497ecbcfcd8777bc762877da43 Mon Sep 17 00:00:00 2001 From: Mathieu Grzybek Date: Mon, 25 May 2026 20:36:34 +0200 Subject: [PATCH 2/3] feat(ring0): adding missing files --- .github/workflows/release.yml | 34 +++++++++++++++++++++++++++ docs/cluster-config-example.yaml | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 docs/cluster-config-example.yaml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..286853b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: Release OCI artifact +on: + push: + tags: + - "v*.*.*" +permissions: + contents: read + packages: write +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Flux CLI + uses: fluxcd/flux2/action@main + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Push ring0 Flux manifests as OCI artifact + run: | + flux push artifact \ + "oci://ghcr.io/${{ github.repository }}:${{ github.ref_name }}" \ + --path=ring0/flux \ + --source="${{ github.repositoryUrl }}" \ + --revision="${{ github.ref_name }}@sha1:${{ github.sha }}" + - name: Tag artifact as latest + run: | + flux tag artifact \ + "oci://ghcr.io/${{ github.repository }}:${{ github.ref_name }}" \ + --tag latest diff --git a/docs/cluster-config-example.yaml b/docs/cluster-config-example.yaml new file mode 100644 index 0000000..6c7a90f --- /dev/null +++ b/docs/cluster-config-example.yaml @@ -0,0 +1,40 @@ +# cluster-config β€” example ConfigMap consumed by Flux HelmRelease.valuesFrom references. +# +# Apply this before bootstrapping Flux (or let task flux create it): +# kubectl apply -f docs/cluster-config-example.yaml +# +# Values marked depend on live infrastructure state. +# Quick reference: +# ts_suffix : tailscale dns status | awk '/suffix =/ {gsub(")","");print $NF}' +# pki_endpoint : https://pki. +# openbao_ca_bundle : base64 < dist/bundle.crt | tr -d '\n' +# announcements_iface : talosctl ... get addresses \ +# | grep "$INSTANCE_MANAGEMENT_SERVICES_IPADDR_CIDR" \ +# | awk '{print $4}' | tail -n1 | awk -F/ '{print $1}' +# dhcp_bind_interface : interface on the management node facing the bootstrap VLAN +# bootstrap_endpoint : http:/// (matchbox assets HTTP server) +# artifacts_file_server : http:/// (Flatcar/Talos artifacts) +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-config + namespace: flux-system +data: + # ---- PKI ---------------------------------------------------------------- + pki_org: "My Org" + pki_endpoint: "https://pki.my-cloud.ts.net" + openbao_ca_bundle: "" + # ---- Tailscale ---------------------------------------------------------- + ts_suffix: "my-cloud.ts.net" + idp_hostname: "idp.my-cloud.ts.net" + # ---- Cilium L2 announcements -------------------------------------------- + announcements_iface: "eth1" + # ---- BMaaS β€” service IPs assigned via CiliumLoadBalancerIPPool ---------- + registry_ip: "192.168.3.4" + tinkerbell_ip: "192.168.3.5" + hookos_ip: "192.168.3.6" + dns_ip: "192.168.3.7" + # ---- Tinkerbell --------------------------------------------------------- + dhcp_bind_interface: "eth0" + bootstrap_endpoint: "http://192.168.2.2/" + artifacts_file_server: "http://192.168.2.2/" From a14e98e67551a81fc08870c59f8c344fd6ce08cb Mon Sep 17 00:00:00 2001 From: Mathieu Grzybek Date: Mon, 25 May 2026 20:37:23 +0200 Subject: [PATCH 3/3] feat(ring0): Patching documentation and gitignore Signed-off-by: Mathieu Grzybek --- .gitignore | 2 ++ README.md | 17 +++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 70a6e78..7fdc687 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,7 @@ ring0/core-services/pki/files *.csr # Tooling +.claude +CLAUDE.md .vscode .task diff --git a/README.md b/README.md index 403e566..8197044 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,12 @@ UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") ```plaintext micro-cloud/ β”œβ”€β”€ ring0/ # Low dependency services and core infrastructure (PKI, Netboot, Management) +β”‚ β”œβ”€β”€ core-services/ # Manifests and templates for ring0 Kubernetes workloads +β”‚ β”œβ”€β”€ flux/ # FluxCD Operator manifests (HelmRepositories, HelmReleases, Kustomizations) +β”‚ └── scripts/ # Bootstrap and day-2 shell scripts β”œβ”€β”€ ring1/ # Experimental environments (Kubernetes clusters, etc.) -β”œβ”€β”€ docs/ # Schematics, documentation, articles +β”œβ”€β”€ docs/ # Schematics, documentation, examples +β”œβ”€β”€ .github/ # GitHub Actions workflows (OCI artifact release) β”œβ”€β”€ LICENSE # Project license (Apache 2.0) └── README.md # This file ``` @@ -83,10 +87,11 @@ The project relies on a home server rack composed of: - **Network / VPN:** Tailscale - **Containerization:** Incus (LXC / KVM) -- **PKI:** cfssl, cert-manager, openbao +- **PKI:** cfssl, cert-manager, OpenBao - **Bootstrapping:** kea, matchbox, Talos -- **Orchestration:** Kubernetes, Kamaji, Tinkerbell -- **Middleware:** Netbox, Authentik +- **GitOps:** FluxCD Operator (OCI artifact releases via GitHub Actions) +- **Orchestration:** Kubernetes, Kamaji, Tinkerbell, Cluster API +- **Middleware:** Netbox, Authentik, External Secrets Operator, Zot ## Getting Started @@ -109,9 +114,9 @@ incus remote add headnode-0 headnode-0 incus remote switch headnode-0 ``` -### 3. Start the bootstrap sequence +### 3. Bootstrap sequence -Refer to [ring0/README.md](ring0/README.md) for step-by-step operations (PKI, netboot, management node). +Refer to [ring0/README.md](ring0/README.md) for the full step-by-step sequence. ## Contributions