From 07d2474752ddc8c3ab890b697c3805e646626109 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Wed, 14 Oct 2026 11:58:21 +0000 Subject: [PATCH 1/2] docs(networking): drop the HTTPCache application The HTTPCache application was removed from the catalog in Cozystack 1.7 (cozystack/cozystack#4200), so packages/apps/http-cache does not exist at v1.7.0: it is gone from release-1.7 and from v1.7.0-rc.1. The NETWORKING generator list still named it, and hack/update_apps.sh gets a 404 fetching its README and exits 1. At promotion that aborts make update-all right after release-next, so no docs PR is opened. The page and its front-matter stub leave next/ for the same reason: release-next copies next/ wholesale into v1.7/. The other mentions in next/ go with them. The ApplicationDefinition naming examples switch to VMInstance, which still shows an acronym kind with a hyphenated name. The NGINX and IP2Location cards only described what HTTPCache bundled, so they go, and HAProxy keeps only TCP Balancer. Released version trees keep their HTTPCache pages, as those releases ship it. Assisted-By: LLM Signed-off-by: Myasnikov Daniil --- Makefile | 2 +- content/en/docs/next/_index.md | 2 +- content/en/docs/next/cozystack-api/_index.md | 1 - .../cozystack-api/application-definitions.md | 38 ++--- content/en/docs/next/guides/concepts.md | 2 +- .../docs/next/guides/platform-stack/_index.md | 4 +- .../next/networking/_include/http-cache.md | 10 -- content/en/docs/next/networking/http-cache.md | 155 ------------------ .../next/operations/configuration/licenses.md | 4 +- 9 files changed, 24 insertions(+), 194 deletions(-) delete mode 100644 content/en/docs/next/networking/_include/http-cache.md delete mode 100644 content/en/docs/next/networking/http-cache.md diff --git a/Makefile b/Makefile index 6bf3d05d8..328fd13b6 100644 --- a/Makefile +++ b/Makefile @@ -76,7 +76,7 @@ APPS ?= tenant clickhouse foundationdb harbor redis mongodb openbao rabbit K8S ?= kubernetes K8S_NODES ?= kubernetes-nodes VMS ?= vm-disk vm-instance -NETWORKING ?= vpc vpn http-cache tcp-balancer +NETWORKING ?= vpc vpn tcp-balancer SERVICES ?= bootbox etcd ingress monitoring seaweedfs SERVICES += computeplane gateway APPS_DEST_DIR ?= content/en/docs/$(DOC_VERSION)/applications diff --git a/content/en/docs/next/_index.md b/content/en/docs/next/_index.md index 97be16f15..740c86882 100644 --- a/content/en/docs/next/_index.md +++ b/content/en/docs/next/_index.md @@ -14,6 +14,6 @@ weight: 5 {{% /warning %}} Cozystack is a free PaaS platform and framework for building clouds -With Cozystack, you can transform your bunch of servers into an intelligent system with a simple REST API for spawning Kubernetes clusters, Database-as-a-Service, virtual machines, load balancers, HTTP caching services, and other services with ease. +With Cozystack, you can transform your bunch of servers into an intelligent system with a simple REST API for spawning Kubernetes clusters, Database-as-a-Service, virtual machines, load balancers, and other services with ease. You can use Cozystack to build your own cloud or to provide a cost-effective development environments. diff --git a/content/en/docs/next/cozystack-api/_index.md b/content/en/docs/next/cozystack-api/_index.md index f005c8a01..007cd9060 100644 --- a/content/en/docs/next/cozystack-api/_index.md +++ b/content/en/docs/next/cozystack-api/_index.md @@ -27,7 +27,6 @@ clickhouses apps.cozystack.io/v1alpha1 true ClickHouse etcds apps.cozystack.io/v1alpha1 true Etcd foundationdbs apps.cozystack.io/v1alpha1 true FoundationDB harbors apps.cozystack.io/v1alpha1 true Harbor -httpcaches apps.cozystack.io/v1alpha1 true HTTPCache infos apps.cozystack.io/v1alpha1 true Info ingresses apps.cozystack.io/v1alpha1 true Ingress kafkas apps.cozystack.io/v1alpha1 true Kafka diff --git a/content/en/docs/next/cozystack-api/application-definitions.md b/content/en/docs/next/cozystack-api/application-definitions.md index c6d418d3d..b6641f4cf 100644 --- a/content/en/docs/next/cozystack-api/application-definitions.md +++ b/content/en/docs/next/cozystack-api/application-definitions.md @@ -40,45 +40,45 @@ the chart referenced by the definition. sets them explicitly, and the relationship between them is **not derivable by any string transform**: -| Field | Style | Example (HTTP cache) | Example (VM disk) | Example (TCP balancer) | +| Field | Style | Example (VM instance) | Example (VM disk) | Example (TCP balancer) | | --- | --- | --- | --- | --- | -| `metadata.name` | lowercase-with-hyphens | `http-cache` | `vm-disk` | `tcp-balancer` | -| `spec.application.kind` | CamelCase, preserves acronyms | `HTTPCache` | `VMDisk` | `TCPBalancer` | -| `spec.application.singular` | lowercase, no hyphens | `httpcache` | `vmdisk` | `tcpbalancer` | -| `spec.application.plural` | lowercase, no hyphens | `httpcaches` | `vmdisks` | `tcpbalancers` | +| `metadata.name` | lowercase-with-hyphens | `vm-instance` | `vm-disk` | `tcp-balancer` | +| `spec.application.kind` | CamelCase, preserves acronyms | `VMInstance` | `VMDisk` | `TCPBalancer` | +| `spec.application.singular` | lowercase, no hyphens | `vminstance` | `vmdisk` | `tcpbalancer` | +| `spec.application.plural` | lowercase, no hyphens | `vminstances` | `vmdisks` | `tcpbalancers` | Note that `metadata.name` is not a function of `spec.application.kind`. The -hyphen positions (`tcp-balancer`, `vm-disk`, `http-cache`) and the absence of -hyphens in `singular`/`plural` (`tcpbalancer`, `vmdisk`, `httpcache`) are +hyphen positions (`tcp-balancer`, `vm-disk`, `vm-instance`) and the absence of +hyphens in `singular`/`plural` (`tcpbalancer`, `vmdisk`, `vminstance`) are conventions chosen per application, not outputs of a shared algorithm. -`strings.ToLower(kind)` yields `httpcache`, which matches +`strings.ToLower(kind)` yields `vminstance`, which matches `spec.application.singular` but **not** `metadata.name`. A direct lookup by the lowercased kind therefore fails: ```bash # The aggregated API resource uses the lowercased plural: -$ kubectl get httpcaches --namespace tenant-demo +$ kubectl get vminstances --namespace tenant-demo NAME READY AGE VERSION frontend True 2m 1.2.0 # But the ApplicationDefinition that backs it is stored under a different name: -$ kubectl get applicationdefinition httpcache -Error from server (NotFound): applicationdefinitions.cozystack.io "httpcache" not found +$ kubectl get applicationdefinition vminstance +Error from server (NotFound): applicationdefinitions.cozystack.io "vminstance" not found -$ kubectl get applicationdefinition http-cache -NAME AGE -http-cache 14d +$ kubectl get applicationdefinition vm-instance +NAME AGE +vm-instance 14d ``` -Acronyms make this more visible: `TCPBalancer`, `HTTPCache`, and `VMDisk` all +Acronyms make this more visible: `TCPBalancer`, `VMInstance`, and `VMDisk` all lose their capitalisation in the aggregated resource name (`tcpbalancers`, -`httpcaches`, `vmdisks`) but keep hyphens in the CRD name (`tcp-balancer`, -`http-cache`, `vm-disk`). +`vminstances`, `vmdisks`) but keep hyphens in the CRD name (`tcp-balancer`, +`vm-instance`, `vm-disk`). ## Recommended lookup pattern Client code that needs to resolve a Cozystack kind — for example a dashboard -that receives `HTTPCache` from a HelmRelease label and wants to render the +that receives `VMInstance` from a HelmRelease label and wants to render the matching form — should **list all `ApplicationDefinition`s and filter by `spec.application.kind`** instead of attempting a direct `Get` by the lowercased kind. The set of definitions is small (tens of items) and changes rarely, so @@ -175,7 +175,7 @@ aggressively a client should cache therefore depends on its own lifetime: driven rolling restart of `cozystack-api` completes. {{% alert color="info" %}} -The lowercased plural (`httpcaches`, `vmdisks`) **is** the correct name for +The lowercased plural (`vminstances`, `vmdisks`) **is** the correct name for tenant-facing resources under `apps.cozystack.io/v1alpha1`. It is only the `applicationdefinitions.cozystack.io` CRD that uses the hyphenated form. {{% /alert %}} diff --git a/content/en/docs/next/guides/concepts.md b/content/en/docs/next/guides/concepts.md index 8d7931809..a2249424d 100644 --- a/content/en/docs/next/guides/concepts.md +++ b/content/en/docs/next/guides/concepts.md @@ -53,7 +53,7 @@ Read more: [tenant Kubernetes clusters]({{% ref "/docs/next/kubernetes" %}}). ## Managed Applications Cozystack comes with a catalog of **managed applications** (services) that can be deployed on the platform with minimal effort. -These include relational databases (PostgreSQL, MySQL/MariaDB), NoSQL/queues (Redis, NATS, Kafka, RabbitMQ), HTTP cache, load balancer, and others. +These include relational databases (PostgreSQL, MySQL/MariaDB), NoSQL/queues (Redis, NATS, Kafka, RabbitMQ), load balancer, and others. Tenants, tenant Kubernetes clusters, and VMs are also managed applications in terms of Cozystack. They are created with the same user workflow and are managed with Helm and Flux, just as other applications. diff --git a/content/en/docs/next/guides/platform-stack/_index.md b/content/en/docs/next/guides/platform-stack/_index.md index 1323c063a..931353b85 100644 --- a/content/en/docs/next/guides/platform-stack/_index.md +++ b/content/en/docs/next/guides/platform-stack/_index.md @@ -129,8 +129,6 @@ Cozystack-maintained charts, CRDs, controllers, and application APIs are license ## Managed networking services {{< oss-cards >}} -{{< oss-card name="NGINX" logo="nginx" license="BSD-2-Clause" source="https://github.com/nginx/nginx/blob/master/LICENSE" description="Powers the managed HTTP Cache service with reverse-proxy caching and GeoIP filtering. Scales horizontally without a shared filesystem." >}} -{{< oss-card name="HAProxy" logo="haproxy" license="GPL-2.0 with exceptions" source="https://github.com/haproxy/haproxy/blob/master/LICENSE" description="Enterprise TCP/HTTP load balancer powering the managed TCP Balancer and HTTP Cache. Provides active health checks and high-throughput connection handling." >}} -{{< oss-card name="IP2Location modules" license="MIT" source="https://github.com/ip2location/ip2location-nginx/blob/master/LICENSE" description="GeoIP modules (IP2Location and IP2Proxy) bundled into the HTTP Cache. Enables country-based traffic filtering and proxy/VPN detection." >}} +{{< oss-card name="HAProxy" logo="haproxy" license="GPL-2.0 with exceptions" source="https://github.com/haproxy/haproxy/blob/master/LICENSE" description="Enterprise TCP/HTTP load balancer powering the managed TCP Balancer. Provides active health checks and high-throughput connection handling." >}} {{< oss-card name="Outline Server (Shadowsocks)" logo="outline" license="Apache-2.0" source="https://github.com/OutlineFoundation/outline-server/blob/master/LICENSE" description="Shadowsocks-based VPN backend developed by Google's Jigsaw team. Manages Shadowsocks instances with symmetric encryption to resist DPI traffic analysis." >}} {{< /oss-cards >}} diff --git a/content/en/docs/next/networking/_include/http-cache.md b/content/en/docs/next/networking/_include/http-cache.md deleted file mode 100644 index e400ad249..000000000 --- a/content/en/docs/next/networking/_include/http-cache.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Managed Nginx-based HTTP Cache Service" -linkTitle: "HTTP Cache" -description: "The Nginx-based HTTP caching service is designed to optimize web traffic and enhance web application performance." -weight: 20 -aliases: - - /docs/reference/applications/http-cache - - /docs/next/reference/applications/http-cache ---- - diff --git a/content/en/docs/next/networking/http-cache.md b/content/en/docs/next/networking/http-cache.md deleted file mode 100644 index 326c981b2..000000000 --- a/content/en/docs/next/networking/http-cache.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -title: "Managed Nginx-based HTTP Cache Service" -linkTitle: "HTTP Cache" -description: "The Nginx-based HTTP caching service is designed to optimize web traffic and enhance web application performance." -weight: 20 -aliases: - - /docs/reference/applications/http-cache - - /docs/next/reference/applications/http-cache ---- - - - - -The Nginx-based HTTP caching service is designed to optimize web traffic and enhance web application performance. -This service combines custom-built Nginx instances with HAProxy for efficient caching and load balancing. - -## Deployment information - -The Nginx instances include the following modules and features: - -- VTS module for statistics -- Integration with ip2location -- Integration with ip2proxy -- Support for 51Degrees -- Cache purge functionality - -HAproxy plays a vital role in this setup by directing incoming traffic to specific Nginx instances based on a consistent hash calculated from the URL. Each Nginx instance includes a Persistent Volume Claim (PVC) for storing cached content, ensuring fast and reliable access to frequently used resources. - -## Deployment Details - -The deployment architecture is illustrated in the diagram below: - -``` - - ┌─────────┐ - │ metallb │ arp announce - └────┬────┘ - │ - │ - ┌───────▼───────────────────────────┐ - │ kubernetes service │ node - │ (externalTrafficPolicy: Local) │ level - └──────────┬────────────────────────┘ - │ - │ - ┌────▼────┐ ┌─────────┐ - │ haproxy │ │ haproxy │ loadbalancer - │ (active)│ │ (backup)│ layer - └────┬────┘ └─────────┘ - │ - │ balance uri whole - │ hash-type consistent - ┌──────┴──────┬──────────────┐ - ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ caching - │ nginx │ │ nginx │ │ nginx │ layer - └───┬───┘ └───┬───┘ └───┬───┘ - │ │ │ - ┌────┴───────┬─────┴────┬─────────┴──┐ - │ │ │ │ - ┌───▼────┐ ┌────▼───┐ ┌───▼────┐ ┌────▼───┐ - │ origin │ │ origin │ │ origin │ │ origin │ - └────────┘ └────────┘ └────────┘ └────────┘ - -``` - -## Known issues - -- VTS module shows wrong upstream response time, [github.com/vozlt/nginx-module-vts#198](https://github.com/vozlt/nginx-module-vts/issues/198) - -## Parameters - -### Common parameters - -| Name | Description | Type | Value | -| -------------- | ------------------------------------------------------------ | ---------- | ------- | -| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` | -| `storageClass` | StorageClass used to store the data. | `string` | `""` | -| `external` | Enable external access from outside the cluster. | `bool` | `false` | - - -### Application-specific parameters - -| Name | Description | Type | Value | -| ----------- | ------------------------------------------------ | ---------- | ----- | -| `endpoints` | Endpoints configuration, as a list of . | `[]string` | `[]` | - - -### HAProxy parameters - -| Name | Description | Type | Value | -| -------------------------- | -------------------------------------------------------------------------------------------------------- | ---------- | ------ | -| `haproxy` | HAProxy configuration. | `object` | `{}` | -| `haproxy.replicas` | Number of HAProxy replicas. | `int` | `2` | -| `haproxy.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `haproxy.resources.cpu` | CPU available to each replica. | `quantity` | `""` | -| `haproxy.resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` | -| `haproxy.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` | - - -### Nginx parameters - -| Name | Description | Type | Value | -| ------------------------ | -------------------------------------------------------------------------------------------------------- | ---------- | ------ | -| `nginx` | Nginx configuration. | `object` | `{}` | -| `nginx.replicas` | Number of Nginx replicas. | `int` | `2` | -| `nginx.resources` | Explicit CPU and memory configuration. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | -| `nginx.resources.cpu` | CPU available to each replica. | `quantity` | `""` | -| `nginx.resources.memory` | Memory (RAM) available to each replica. | `quantity` | `""` | -| `nginx.resourcesPreset` | Default sizing preset used when `resources` is omitted. | `string` | `nano` | - - -## Parameter examples and reference - -### resources and resourcesPreset - -`resources` sets explicit CPU and memory configurations for each replica. -When left empty, the preset defined in `resourcesPreset` is applied. - -```yaml -resources: - cpu: 4000m - memory: 4Gi -``` - -`resourcesPreset` sets named CPU and memory configurations for each replica. -This setting is ignored if the corresponding `resources` value is set. - -| Preset name | CPU | memory | -|-------------|--------|---------| -| `nano` | `250m` | `128Mi` | -| `micro` | `500m` | `256Mi` | -| `small` | `1` | `512Mi` | -| `medium` | `1` | `1Gi` | -| `large` | `2` | `2Gi` | -| `xlarge` | `4` | `4Gi` | -| `2xlarge` | `8` | `8Gi` | - - -### endpoints - -`endpoints` is a flat list of IP addresses: - -```yaml -endpoints: - - 10.100.3.1:80 - - 10.100.3.11:80 - - 10.100.3.2:80 - - 10.100.3.12:80 - - 10.100.3.3:80 - - 10.100.3.13:80 -``` diff --git a/content/en/docs/next/operations/configuration/licenses.md b/content/en/docs/next/operations/configuration/licenses.md index b5cc17aaa..4c844e3ee 100644 --- a/content/en/docs/next/operations/configuration/licenses.md +++ b/content/en/docs/next/operations/configuration/licenses.md @@ -127,8 +127,6 @@ Pinned upstream versions of managed runtimes (PostgreSQL, MariaDB, Kafka, etc.) ## Managed networking services {{< oss-cards >}} -{{< oss-card name="NGINX" logo="nginx" license="BSD-2-Clause" source="https://github.com/nginx/nginx/blob/master/LICENSE" description="Used by the managed HTTP Cache service." >}} -{{< oss-card name="HAProxy" logo="haproxy" license="GPL-2.0 with exceptions" source="https://github.com/haproxy/haproxy/blob/master/LICENSE" description="Used by the managed TCP Balancer and HTTP Cache services." >}} -{{< oss-card name="IP2Location modules" license="MIT" source="https://github.com/ip2location/ip2location-nginx/blob/master/LICENSE" description="GeoIP modules bundled into the HTTP Cache (IP2Location and IP2Proxy)." >}} +{{< oss-card name="HAProxy" logo="haproxy" license="GPL-2.0 with exceptions" source="https://github.com/haproxy/haproxy/blob/master/LICENSE" description="Used by the managed TCP Balancer service." >}} {{< oss-card name="Outline Server (Shadowsocks)" logo="outline" license="Apache-2.0" source="https://github.com/OutlineFoundation/outline-server/blob/master/LICENSE" description="Backs the managed VPN service." >}} {{< /oss-cards >}} From 439a646ca64436cee9e0613fe92a2a842c65e8b8 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Wed, 14 Oct 2026 11:59:33 +0000 Subject: [PATCH 2/2] docs(networking): temporarily remove the Cloudflare Tunnel page The Cloudflare Tunnel Gateway API package (cozystack/cozystack#3858) is not merged and does not ship in Cozystack 1.7. At promotion, release-next copies next/ wholesale into v1.7/, so the page would be published as v1.7 documentation for a package that release lacks. This takes out the page and its licenses card, which came in together in 9b9965a8, and the one cross-link to the page in gateway-api.md, where the edge certificate mode named the tunnel as its worked example. Revert this commit once the package ships, to restore the page, the card and the link as they were. Assisted-By: LLM Signed-off-by: Myasnikov Daniil --- .../docs/next/networking/cloudflare-tunnel.md | 227 ------------------ .../en/docs/next/networking/gateway-api.md | 2 +- .../next/operations/configuration/licenses.md | 1 - 3 files changed, 1 insertion(+), 229 deletions(-) delete mode 100644 content/en/docs/next/networking/cloudflare-tunnel.md diff --git a/content/en/docs/next/networking/cloudflare-tunnel.md b/content/en/docs/next/networking/cloudflare-tunnel.md deleted file mode 100644 index 5f692f444..000000000 --- a/content/en/docs/next/networking/cloudflare-tunnel.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: "Publishing through a Cloudflare Tunnel" -linkTitle: "Cloudflare Tunnel" -description: "Optional system package registering a second Gateway API implementation whose Gateways are published by Cloudflare over an outbound tunnel, for clusters with no routable inbound address." -weight: 16 ---- - -## What this page covers - -What the `cloudflare-tunnel-gateway-controller` package is and when it is the right tool, what has to exist on the Cloudflare side before enabling it, the credentials Secret, the two-step enable, how a Gateway joins the class, the limitations that should decide whether to enable it at all, and the removal path. - -## What it is - -`cloudflare-tunnel-gateway-controller` is an optional system package that registers a second Gateway API implementation alongside the Cilium one. A Gateway on its class is published by Cloudflare: clients reach Cloudflare's edge, and the edge carries the request into the cluster over a Cloudflare Tunnel that the in-cluster data plane opens outbound. The Services the chart renders are `ClusterIP` — the two proxy Services unconditionally, the controller's own by a `service.type` value that defaults to it — so nothing on this path asks the cluster for an address reachable from outside. - -GatewayClasses are cluster-scoped and each Gateway names the one it wants, so enabling this package does not move anything already published through Cilium. - -## When to reach for it - -The [Gateway a tenant publishes through]({{% ref "/docs/next/networking/gateway-api" %}}) is backed by Cilium unless the tenant is moved onto another class, and that path wants two things from the outside world — an address clients can open a connection to, and an ACME challenge that completes against the cluster. The tunnel class drops both for the hostnames it serves: inbound connections are replaced by an outbound connection from the proxy pods to Cloudflare's edge, and TLS is terminated at the edge rather than on a Gateway listener. - -It fits a cluster behind NAT or CGNAT, a lab or home cluster with no routable prefix, a site whose firewall will not forward ports, or a deployment that wants Cloudflare's edge in front of a subset of hostnames. - -It is the wrong tool when you need TLS passthrough or any non-HTTP protocol, when the certificate a client sees has to be one the cluster controls, or when a third party in the request path is unacceptable. - -## What has to exist on the Cloudflare side - -1. A zone whose DNS is served by Cloudflare, covering the hostnames you intend to publish. -2. A Tunnel, created under **Zero Trust → Networks → Tunnels** with the `cloudflared` connector type. Keep both its **Tunnel ID** and its **tunnel token**. -3. An API token with the **Account → Cloudflare Tunnel → Edit** permission. - -{{% alert title="Important" color="warning" %}} - -Give the controller a tunnel of its own. Its upstream documentation states that it assumes exclusive ownership of the tunnel configuration, performs a full synchronization on startup, and removes ingress rules that do not come from routes it manages — so a tunnel that also carries hand-written public hostnames, or one shared with another system, loses them. - -{{% /alert %}} - -The Cloudflare account ID is auto-detected when the API token has access to a single account. When it does not, supply it explicitly, either as an `account-id` key in the Secret below or through the chart's `gatewayClassConfig.accountId` value. - -## The credentials Secret - -Both planes read one Secret, named `cloudflare-tunnel-credentials`, in the package namespace `cozy-cloudflare-tunnel-gateway-controller`: - -| Key | Read by | Contents | -| --- | --- | --- | -| `api-token` | the controller | the Cloudflare API token | -| `tunnel-token` | the proxy, as the `TUNNEL_TOKEN` environment variable | the tunnel's connector token | -| `account-id` | the controller, optional | account ID, when auto-detection cannot pick one | - -This is a different credential from the Cloudflare API token used by the DNS-01 ACME solver (`publishing.certificates.dns01.cloudflare.secretName`, default `cloudflare-api-token-secret`) — that one needs zone DNS permissions, this one needs tunnel permissions. Do not assume one Secret can serve both. - -Create it before or shortly after enabling the package, because the proxy pod cannot start without it: - -```bash -kubectl create namespace cozy-cloudflare-tunnel-gateway-controller \ - --dry-run=client --output yaml | kubectl apply --filename - - -kubectl --namespace cozy-cloudflare-tunnel-gateway-controller \ - create secret generic cloudflare-tunnel-credentials \ - --from-literal=api-token="$CF_API_TOKEN" \ - --from-literal=tunnel-token="$CF_TUNNEL_TOKEN" -``` - -## Enabling the package - -The package is in no bundle by default, because it cannot become Ready without operator input. Enabling it takes two edits, and the second lands on an object that exists only after the first. - -First, list it in `bundles.enabledPackages` on the platform values: - -```yaml -apiVersion: cozystack.io/v1alpha1 -kind: Package -metadata: - name: cozystack.cozystack-platform -spec: - components: - platform: - values: - bundles: - enabledPackages: - - cozystack.cloudflare-tunnel-gateway-controller -``` - -The name also has to stay out of `bundles.disabledPackages`. The two lists are checked in different places — the bundle tests `enabledPackages` before it calls the helper that renders the Package, and that helper then vetoes on `disabledPackages` — so a name present in both is not emitted, whichever order you think of them in. - -That render creates a `Package` named `cozystack.cloudflare-tunnel-gateway-controller`. Second, set the tunnel ID on it: - -```yaml -apiVersion: cozystack.io/v1alpha1 -kind: Package -metadata: - name: cozystack.cloudflare-tunnel-gateway-controller -spec: - components: - cloudflare-tunnel-gateway-controller: - values: - cloudflare-tunnel-gateway-controller: - gatewayClassConfig: - tunnelID: "00000000-0000-4000-8000-000000000000" -``` - -Until that value is set the chart refuses to render and the HelmRelease reports `gatewayClassConfig.tunnelID is required`. Because the Package appears only once the name is in `enabledPackages`, the first reconcile after enabling always fails this way; setting the tunnel ID changes the values and the install is retried. While that window is open the cluster carries a HelmRelease that is not Ready. Nothing in the platform gates on that, but the platform does ship the instrument most likely to notice: `check-readiness` counts `helmreleases.helm.toolkit.fluxcd.io` among the resources whose `Ready` condition it requires, so it reports the cluster as not ready until the tunnel ID is in place, and `check-readiness --wait` blocks until everything is ready or exits non-zero at its timeout (30 minutes by default). Have the tunnel ID ready before enabling if anything in your automation waits on that. - -The platform writes one key of its own inside that same component values block, `controller.clusterDomain`, taken from `networking.clusterDomain`, because the vendored chart bakes the cluster domain into the proxy's config-endpoint URL and falls back to `cluster.local` when it is empty — wrong on a Cozystack default of `cozy.local`. - -Editing a Package the platform renders sounds like it should be undone by the next platform reconcile, and it is not. The tunnel ID is a sibling key of the one the platform writes, and the two have different owners: helm-controller applies Packages server-side, where field ownership is tracked per field and the platform owns only what it renders, and a release still on client-side apply patches only its own rendered fields for the same result. A platform values edit or version bump therefore leaves `gatewayClassConfig.tunnelID` alone. There is no platform-values path for it — the Package is where it lives, which is why enabling takes two edits rather than one. - -## The GatewayClass, and attaching a Gateway - -The package creates one `GatewayClass` named `cloudflare-tunnel`, with `spec.controllerName: cf.k8s.lex.la/tunnel-controller` and a `parametersRef` to the cluster-scoped `GatewayClassConfig` where the tunnel ID and the credentials reference land. The class name is cosmetic **to the controller**, which binds its GatewayClasses by `controllerName` — but not to Cozystack: `gateway.className`, `gateway.tenantSelectableClasses` and `gateway.edgeTerminatedClasses` all match on the name, trimmed but never case-folded. Renaming the class means updating those lists, and how that failure surfaces depends on which list is stale — a `className` no controller claims leaves the Gateway unprogrammed, a dropped `tenantSelectableClasses` entry fails the tenant's release with a message naming the class, and only a stale `edgeTerminatedClasses` entry is genuinely silent. See [GatewayClass names that match no installed class]({{% ref "/docs/next/networking/gateway-api#gatewayclass-names-that-match-no-installed-class" %}}). - -A Gateway joins the class by naming it, and routes attach to that Gateway the usual way: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: tunnel - namespace: tenant-example -spec: - gatewayClassName: cloudflare-tunnel - listeners: - - name: https - protocol: HTTPS - port: 443 ---- -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: app - namespace: tenant-example -spec: - parentRefs: - - name: tunnel - hostnames: - - app.example.com - rules: - - backendRefs: - - name: app - port: 80 -``` - -The listener also declares no `hostname`, which is deliberate: the Gateway serves whatever its routes claim, and the platform's Gateway-listener admission policy allows a hostname-less listener rather than requiring one. Give it a hostname and that hostname must fall within the namespace's `namespace.cozystack.io/host` apex. - -The listener is `protocol: HTTPS` and carries no `tls` block, which is deliberate and is the shape the chart's own installation notes prescribe: TLS ends at Cloudflare's edge, so there is no certificate for this Gateway to present. Gateway API permits it — `tls` is not a required listener field, and the CRD's only rule tying the two together constrains `tls.mode` for an HTTPS listener that *has* a `tls` block, so one without it is admitted. What the listener declares here is which routes bind to it — the port and protocol are route-binding inputs on this class, not a statement about what Cloudflare's edge serves. That is why the port-80 `HTTP` shape an edge-terminated `TenantGateway` renders works on this class too. - -Routes are `HTTPRoute` and `GRPCRoute`. A backend is normally a Service, and a Service of type `ExternalName` works for an out-of-cluster origin — the proxy infers the scheme from the backendRef port. The chart also installs an `ExternalBackend` CRD for the cases that shape cannot cover: it makes the scheme explicit rather than inferred, lets the host be an address that is not a valid Service name, and can prepend a base path to the request. - -The controller writes the tunnel's CNAME target, `.cfargotunnel.com`, into the Gateway's `status.addresses`. Turning that into a DNS record needs an `external-dns` that watches Gateway API, which is not how Cozystack ships either of them: the system package `cozystack.external-dns` runs the upstream chart's default sources, `service` and `ingress`, and surfaces no value of its own for adding more — a Package's component values do pass through untyped, so `sources` can still be set that way, but it is undocumented rather than supported. The per-tenant `external-dns` application does have the switch — `gatewayAPI: true` adds the `gateway-httproute` and `gateway-tlsroute` sources — and it is `false` by default. Neither path adds `gateway-grpcroute`, so a hostname published only by a `GRPCRoute` gets no record from either. Failing all that, create the CNAME in the Cloudflare zone yourself, proxied. - -## Which Gateway to attach - -On its default settings the per-tenant Gateway that `tenant.spec.gateway: true` renders is not a drop-in for this class. Its `tlsPassthroughServices` become `protocol: TLS`, `mode: Passthrough` listeners, a shape this controller has nothing to map onto — and a listener it does not accept holds the whole object at `Ready=False`, because cozystack-controller marks a `TenantGateway` Ready only when every listener reports both `Accepted` and `Programmed`. The remaining port-443 listeners are rendered `mode: Terminate` with a `certificateRefs` entry — cert-manager's under the ACME modes, the operator's Secret under `existingSecret` — which either way is the shape this class does not use, because the edge already terminates. - -Putting the class into `gateway.edgeTerminatedClasses` resolves all of that. A tenant Gateway on an edge-terminated class renders port-80 `HTTP` listeners only, with no `tls` block, no `certificateRefs` and no passthrough listeners — see [`edge` cert mode]({{% ref "/docs/next/networking/gateway-api#edge-tls-terminated-by-the-class-provider" %}}) for the full shape and for the platform values that select it. That is a Gateway this controller can serve, so the combination works; the standalone Gateway shown above is simply the path that needs no platform configuration. - -Two caveats survive the switch. Edge listeners pin `allowedRoutes.kinds` to `HTTPRoute` alone, so a `GRPCRoute` cannot attach to an edge `TenantGateway` — a route of that kind needs the standalone Gateway. And selecting the class through `gateway.className` rather than per-tenant `tenant.spec.gatewayClass` puts the publishing tenant on it too. Where the platform's TLS-passthrough endpoints are published at all — each renders its `TLSRoute` only when `gateway.enabled` is on and its name is in `publishing.exposedServices` — that unpublishes them, with nothing on the Gateway or the `TenantGateway` reporting it; put child tenants on the class instead. - -Two platform values are needed for that, not one. `gateway.edgeTerminatedClasses` is what selects the certificate behaviour, and `gateway.tenantSelectableClasses` is what lets a tenant name the class at all — a tenant may only name the current `gateway.className` or something on that list, and anything else fails that tenant's own gateway release at render time. See [picking a GatewayClass]({{% ref "/docs/next/networking/gateway-api#picking-a-gatewayclass" %}}). Bear in mind that a child tenant's apex is one label below its parent's, so its application hostnames sit two labels below the zone — past what Cloudflare's Universal SSL covers, per the certificate limitation below. - -## Hostname ownership between tenants - -By default every Gateway of this class shares one tunnel and one proxy pool, so the data plane picks a backend by hostname across all of them — a route claiming another tenant's hostname would be answered rather than ignored, which is not the case behind a per-tenant Cilium address. - -Cozystack's own hostname policies do not close that on its own: the pair — `cozystack-route-hostname-policy` for `HTTPRoute` and `cozystack-route-hostname-policy-tls` for `TLSRoute` — binds route hostnames to the namespace's `namespace.cozystack.io/host` label, but between them they cover only those two kinds in namespaces whose name starts with `tenant-`, neither sees `GRPCRoute`, and both admit a route that declares no hostnames at all. - -So the package turns on the chart's own hostname-ownership layer, keyed on the same `namespace.cozystack.io/host` label and scoped to every namespace that carries it. On this class, a route in such a namespace is rejected by the controller unless it declares hostnames explicitly and each one equals the label value or is a subdomain of it. A leading `*.` is stripped before that comparison, so a wildcard hostname is allowed on the same terms — `*.