Skip to content

Releases: JonathanGraniero/kflare

kflare v0.4.1

Choose a tag to compare

@github-actions github-actions released this 01 Nov 00:19
4c3600c

The sixth alpha release of kflare: the API is still v1alpha1, and it needs Kubernetes 1.34 or newer. It fixes a race
in the shared CRUD reconciler that v0.4.0 shipped with, which could upload a Worker twice, or mark an object kflare
created as adopted.

helm upgrade kflare oci://ghcr.io/jonathangraniero/charts/kflare --version 0.4.1 -n kflare-system \
  --values kflare-values.yaml

The image is ghcr.io/jonathangraniero/kflare:0.4.1, for linux/amd64 and linux/arm64. The deploy guide's
Verifying a release checks both signatures before you install. To install kflare
for the first time, follow the README's quickstart.

Upgrading from v0.4.0

The CRDs, the controller's permissions and the chart's defaults are v0.4.0's, so the upgrade itself needs nothing.

A resource with adopt-or-create that v0.4.0 marked kflare.dev/adopted for an object it created stays marked, and
deleting it keeps the object, as for any adopted object. To have such an object deleted with its resource, set
kflare.dev/deletion-policy: delete on the resource first.

The controller

  • A resource no longer acts on a stale read of itself. The manager's cache can lag behind the status a reconcile
    wrote last, while that reconcile's own writes queue the next one. Reading no ID, the next reconcile found the object
    it had just created and wrote it again, or, with adopt-or-create, marked it adopted, so that deleting the resource
    would have kept it; reading a WorkerScript's previous hash, it uploaded the Worker a second time. The kinds of the
    shared CRUD reconciler now read the resource from the API server before they find, adopt, create or update an
    object, and wait for the cache when it is behind (#131)

Known limitations

v0.4.0's, unchanged: ARCHITECTURE.md explains each.

kflare v0.4.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 21:43
298c8f7

Known issue, fixed in v0.4.1. A reconcile can act on a copy of its resource from before the status the previous reconcile wrote. A WorkerScript can then upload its Worker a second time, and with adopt-or-create a resource can mark an object it created as adopted, so that deleting the resource keeps the object. The fix is #131, on main.

The fifth alpha release of kflare: the API is still v1alpha1, and it needs Kubernetes 1.34 or newer. Every kind but
CloudflareAccount now runs on a shared reconciler. IPList, Tunnel and DNSRecord are generated by kflare's code
generator, on Cloudflare's generated Go SDK; Zone, TunnelConfiguration and WorkerScript get their types and
wiring from it, while their fields and calls stay hand-written on cloudflare-go v0.119. Manifests written for v0.3
apply unchanged
, but these kinds behave as the generated kinds do, and the chart now creates a
HorizontalPodAutoscaler, on by default: read Upgrading from v0.3.0 before upgrading. It is a
minor release because of those changes.

helm upgrade kflare oci://ghcr.io/jonathangraniero/charts/kflare --version 0.4.0 -n kflare-system \
  --values kflare-values.yaml

The image is ghcr.io/jonathangraniero/kflare:0.4.0, for linux/amd64 and linux/arm64. The deploy guide's
Verifying a release checks both signatures before you install. To install kflare
for the first time, follow the README's quickstart.

Upgrading from v0.3.0

No manifest changes, and the controller's permissions are v0.3.0's. Of the CRDs, only Tunnel's and WorkerScript's
change, as Every kind on a shared reconciler says. If you manage the CRDs yourself
(crds.enabled=false), apply config/crd/bases from v0.4.0 first. Each existing resource keeps its Cloudflare object:
it records its claim at its first reconcile, without being marked adopted, and no Worker is uploaded again.

The chart creates a HorizontalPodAutoscaler, on by default, which then owns the Deployment's replica count:

  • replicaCount is ignored while autoscaling.enabled is true. If you set replicaCount: 2 to keep a standby,
    set autoscaling.minReplicas: 2 instead, or autoscaling.enabled: false to go on using replicaCount
  • The autoscaler needs the metrics API, which metrics-server provides. Without it, the Deployment keeps
    autoscaling.minReplicas replicas, one by default
  • The controller's resources change: its CPU request from 10m to 100m, which the autoscaler's target is a share
    of, a CPU limit of 1, and its memory limit from 256Mi to 512Mi. Values that set resources replace only the keys
    they set

Every kind on a shared reconciler

IPList, Tunnel, DNSRecord, Zone, TunnelConfiguration and WorkerScript move onto the shared CRUD reconciler.
They keep their spec fields, and now behave as the generated kinds do:

  • A Tunnel's spec.name can change. It renames the tunnel in place, which cloudflare-go v0.119 could not do
  • Status gains status.cloudflareMetadata.scriptName on a WorkerScript, set by its next upload
  • Tunnel, TunnelConfiguration and WorkerScript claim their objects, in the labels kflare.dev/tunnel-id,
    kflare.dev/tunnel-configuration-id and kflare.dev/worker-name. A resource that would take over an object another
    one claims reports NameConflict, or TunnelAlreadyConfigured, rather than sharing it. A Zone records its zone in
    kflare.dev/zone-id, but two Zones may still name one Cloudflare zone, as they could before
  • A DNSRecord reports NameConflict for an identical record another DNSRecord manages, where it used to try a
    create that Cloudflare refuses
  • A new Tunnel sends no tunnel_secret. It is the password of a locally-managed tunnel; cloudflared runs a
    remotely-managed one with the token, which Cloudflare issues all the same (checked 2026-10-30)
  • A resource whose status was never written still deletes its object, through its claim, as only IPList did
  • ListInUse's message is the shared one: the list, what to do and how to keep it. Its reason does not change

The Helm chart

  • A HorizontalPodAutoscaler (autoscaling), on by default: from 1 to 10 replicas, adding them once the pods'
    average CPU use has been above 80% of their request for 2 minutes. Only the leader reconciles, so the replicas it
    adds are standbys, which take over sooner if the leader goes; they add no reconcile capacity. Memory is left out
    by default, since the leader caches every Secret and the standbys nothing. The deploy guide's
    Scaling and resources says how it behaves and how to tune it
  • Resources from measurements: requests of 100m CPU and 64Mi memory, limits of 1 CPU and 512Mi memory. The
    deploy guide lists what the controller uses idle, with 1,000 and 5,000 resources, while taking over from a leader
    and with many Secrets, and how to size its memory for a cluster's Secrets
    (#70, its sizing part)
  • The PodDisruptionBudget is created whenever the Deployment can run more than one replica: with autoscaling on,
    when autoscaling.maxReplicas is above 1, so a default install gets one. With maxUnavailable: 1 it never blocks
    a drain of a single replica
  • The chart refuses autoscaling with maxReplicas above 1 and leaderElection: false, since every replica would
    then reconcile, and a CPU or memory target without the matching request

Development

  • hack/sizing measures the controller's CPU and memory: it runs the image in Docker against an envtest API server,
    as a leader and a standby, while it adds Secrets and kflare resources and stops the leader
  • Go code that builds these kinds' specs changes: their fields moved into inline structs, such as
    DNSRecordSpecExtras, so a composite literal names them. Reading spec.tunnelRef from Go does not change
  • The samples of IPList, Tunnel and DNSRecord are generated now, so config/samples names other objects

Known limitations

v0.2.0's, but for Tunnel renames, which now work: ARCHITECTURE.md explains each.
The limits of cloudflare-go v0.119 now apply only to Zone, TunnelConfiguration and WorkerScript.

kflare v0.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 22:52
97b5037

The fourth alpha release of kflare: the API is still v1alpha1, and it needs Kubernetes 1.34 or newer. Five more kinds
are now generated by kflare's code generator, on Cloudflare's generated Go SDK, as the Rules products' kinds already
were: the three security rule kinds, WAFCustomRule, RateLimitRule and ManagedRuleset, so that every rule kind is
generated, and R2Bucket and WorkerRoute, the first of the kinds written before the generator to move onto it.
The rule kinds' fields take the names of Cloudflare's API, so manifests written for v0.2 change: read
Upgrading from v0.2.1 before upgrading. It is a minor release because of that change.
R2Bucket and WorkerRoute apply manifests unchanged, but behave differently in the ways their sections list. The
other kinds written before the generator (Zone, DNSRecord, Tunnel, TunnelConfiguration, WorkerScript and
IPList) are unchanged: they move onto it in later releases, whose notes list their changes the same way. The chart
also gains an Artifact Hub page with its license, CRDs and security report.

helm upgrade kflare oci://ghcr.io/jonathangraniero/charts/kflare --version 0.3.0 -n kflare-system \
  --values kflare-values.yaml

The image is ghcr.io/jonathangraniero/kflare:0.3.0, for linux/amd64 and linux/arm64. The deploy guide's
Verifying a release checks both signatures before you install. To install kflare
for the first time, follow the README's quickstart.

Upgrading from v0.2.1

The chart's values and the controller's permissions are v0.2.1's, and so are the other CRDs but R2Bucket's and
WorkerRoute's, whose manifests need no change. Only the WAFCustomRule, RateLimitRule and ManagedRuleset
resources do; if this lists none, they need nothing:

kubectl get wafcustomrules,ratelimitrules,managedrulesets -A

Check your R2Buckets before upgrading as well. spec.storageClass defaults to Standard, and kflare now sets a
bucket's storage class: a bucket whose class was set to InfrequentAccess outside kflare goes back to Standard
unless its R2Bucket says InfrequentAccess. Pause such an R2Bucket before upgrading
(kubectl annotate r2bucket <name> kflare.dev/paused=true), apply its manifest with storageClass: InfrequentAccess
after the chart, then remove the annotation (kubectl annotate r2bucket <name> kflare.dev/paused-). An alert on a
WorkerRoute's PatternOutsideZone reason needs changing too (WorkerRoute).

For the three rule kinds:

  1. Rewrite their manifests in the new shape, from the sections below, each with a manifest before and after. Keep
    them back for now: the v0.2.1 CRDs refuse the new fields (unknown field "spec.ratelimit")
  2. Pause each WAFCustomRule with a blockResponse, with kubectl annotate wafcustomrule <name> kflare.dev/paused=true. Upgraded, such a rule loses its response, and kflare would write it again without one
  3. Upgrade the chart, which upgrades the CRDs. If you manage the CRDs yourself (crds.enabled=false), apply
    config/crd/bases from v0.3.0 first
  4. Apply the rewritten manifests right away, then remove the annotations from step 2, with
    kubectl annotate wafcustomrule <name> kflare.dev/paused-. With Argo CD or Flux, sync them after the chart

Between steps 3 and 4, a stored resource has lost its old fields: the API server drops the fields the new CRD does not
know when it reads the resource. The RateLimitRule and WAFCustomRule sections below say what kflare does with
their Cloudflare rules until you apply the new shape. Until then a change to the resource's spec is refused
(spec.id: Required value), but one to its metadata or status is not, so it can still be paused, report its status
and be deleted.

ManagedRuleset

  • rulesetID is now id, the API's name for the ruleset an execute rule runs. A ManagedRuleset stored with
    rulesetID has no id after the upgrade, and cannot be updated until it gets one: apply your ManagedRuleset
    manifests with id right after upgrading
  • id can change in place, which updates the deployment's rule to run the other ruleset; it was immutable
  • The category and rule overrides are sent in the spec's order rather than sorted. Cloudflare keeps that order, so
    reordering them in the spec writes the rule again
  • An empty overrides.categories or overrides.rules is refused: leave it out
  • For Go clients: ManagedRulesetCategoryOverride and ManagedRulesetRuleOverride are now
    ManagedRulesetOverridesCategory and ManagedRulesetOverridesRule, and an override's action is a string

Before:

spec:
  zoneRef:
    name: example-zone
  rulesetID: 77454fe2d30c4220b5701f6fdfb893ba
  expression: "true"
  overrides:
    categories:
      - category: wordpress
        action: log

After:

spec:
  zoneRef:
    name: example-zone
  id: 77454fe2d30c4220b5701f6fdfb893ba
  expression: "true"
  overrides:
    categories:
      - category: wordpress
        action: log

RateLimitRule

  • The rate limit moves under ratelimit, the API's name for it: characteristics, period, requestsPerPeriod,
    mitigationTimeout, countingExpression and requestsToOrigin. blockResponse is now response. A
    RateLimitRule stored with the old fields has no rate limit after the upgrade, and cannot be updated until it gets
    one: apply your RateLimitRule manifests in the new shape right after upgrading. Until then its Cloudflare rule is
    left as it was
  • Nothing defaults any more: set action (was block), ratelimit.characteristics (was [ip.src]),
    ratelimit.period (was 10), ratelimit.mitigationTimeout (was the period; 0 throttles, on Enterprise plans) and
    response.statusCode (was 429)
  • ratelimit.characteristics must list cf.colo.id, which kflare no longer adds for you, and at least one other
    field. They are sent sorted
  • For Go clients: RateLimitRuleBlockResponse is now RateLimitRuleResponse, the rate limit is a
    RateLimitRuleRatelimit, its characteristics are []string, and its integers are int64, the optional ones
    pointers

Before:

spec:
  zoneRef:
    name: example-zone
  expression: 'starts_with(http.request.uri.path, "/api/")'
  requestsPerPeriod: 100
  blockResponse:
    contentType: application/json
    content: '{"error":"rate limited"}'

After:

spec:
  zoneRef:
    name: example-zone
  expression: 'starts_with(http.request.uri.path, "/api/")'
  action: block
  ratelimit:
    characteristics: [cf.colo.id, ip.src]
    period: 10
    requestsPerPeriod: 100
    mitigationTimeout: 10
  response:
    statusCode: 429
    contentType: application/json
    content: '{"error":"rate limited"}'

WAFCustomRule

  • A skip rule's parameters move to the spec's root, as the API names them: skip.remainingRules: true is now
    ruleset: current, and skip.phases and skip.products are phases and products. skip.log is now
    logging.enabled
    , and blockResponse is now response
  • A WAFCustomRule stored with the old fields loses them after the upgrade:
    • A skip rule then has no parameters, which Cloudflare refuses (20115): kflare reports TerminalError and leaves
      the Cloudflare rule as it was until you apply the manifest in the new shape
    • A block rule with a blockResponse becomes a plain block rule, and kflare writes its Cloudflare rule again
      without the response. Pause such rules (kflare.dev/paused: "true") before upgrading, apply them in the new
      shape, then remove the annotation
  • Nothing defaults any more: set response.statusCode (was 403). A skip rule without logging still logs its
    matches, Cloudflare's default, which kflare no longer sends itself
  • New: phase: current skips the rest of the phase, the custom rulesets that run later in it included. It combines
    with products, but not with phases or ruleset
  • For Go clients: WAFCustomRuleSkip, WAFCustomRuleSkipPhase, WAFCustomRuleSkipProduct and
    WAFCustomRuleBlockResponse are gone. The phases and products are []string, the response is a
    WAFCustomRuleResponse whose statusCode is an int64, and logging is a WAFCustomRuleLogging

Before:

spec:
  zoneRef:
    name: example-zone
  expression: 'http.request.uri.path eq "/healthz"'
  action: skip
  skip:
    remainingRules: true
    phases: [http_ratelimit]
    log: false
---
spec:
  zoneRef:
    name: example-zone
  expression: 'http.request.uri.path wildcard "/admin*"'
  action: block
  blockResponse:
    contentType: text/plain
    content: Blocked

After:

spec:
  zoneRef:
    name: example-zone
  expression: 'http.request.uri.path eq "/healthz"'
  action: skip
  ruleset: current
  phases: [http_ratelimit]
  logging:
    enabled: false
---
spec:
  zoneRef:
    name: example-zone
  expression: 'http.request.uri.path wildcard "/admin*"'
  action: block
  response:
    statusCode: 403
    contentType: text/plain
    content: Blocked

R2Bucket

  • jurisdiction: default, eu or us keep the bucket's objects in that jurisdiction (fedramp and
    fedramp-high need a FedRAMP account). Each jurisdiction has buckets of its own, so two R2Buckets can name buckets
    of the same name in different jurisdictions. It defaults to default, the only jurisdiction kflare reached before,
    and cannot change
  • storageClass: Standard or InfrequentAccess, the class of objects uploaded to the bucket without one of
    their own, changed in place. It defaults to Standard, so a bucket whose class was set to InfrequentAccess
    outside kflare is set back to Standard
    unless its R2Bucket says InfrequentAccess
  • A WorkerScript d...
Read more

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 26 Oct 21:11
4f29936

The third alpha release of kflare: the API is still v1alpha1, and it needs Kubernetes 1.34 or newer. It is the first
signed release, with SBOMs and provenance for the image, and any resource can now be paused with an annotation.

helm upgrade kflare oci://ghcr.io/jonathangraniero/charts/kflare --version 0.2.1 -n kflare-system \
  --values kflare-values.yaml

The image is ghcr.io/jonathangraniero/kflare:0.2.1, for linux/amd64 and linux/arm64. The deploy guide's
Verifying a release checks both signatures before you install. To install kflare
for the first time, follow the README's quickstart.

Upgrading from v0.2.0

Nothing to change: the CRDs, the controller's permissions and the chart's defaults are v0.2.0's.

Releases

  • The controller image and the chart are signed with cosign, keyless: Sigstore issues the signing certificate to
    kflare's release workflow, for the release's tag, and records the signature in its public transparency log. Each
    platform's image comes with an SPDX SBOM and SLSA provenance, stored in the signed image index, and the GitHub
    release has the SBOMs as files, each with a Sigstore bundle. The deploy guide's
    Verifying a release has the commands, and a Kyverno policy that admits only
    signed kflare images (#84)

The Helm chart

  • image.digest runs the controller image by digest instead of by tag: set it to the digest whose signature you
    verified

The controller

  • Pausing a resource: with the annotation kflare.dev/paused: "true", kflare leaves the resource and its Cloudflare
    object alone, neither reverting changes made in Cloudflare nor applying changes to the spec, until the annotation is
    removed. A paused resource being deleted keeps its finalizer until then. Ready keeps its status and takes the
    reason Paused (#81)

Known limitations

v0.2.0's, unchanged: ARCHITECTURE.md explains each.

v0.2.0

Choose a tag to compare

@JonathanGraniero JonathanGraniero released this 26 Oct 02:01
17312e6

The second alpha release of kflare: the API is still v1alpha1, and it needs Kubernetes 1.34 or newer. It hardens
the Helm chart for production (#67), bounds every call to
Cloudflare with timeouts, can check every resource for drift on a schedule, records Kubernetes Events, and adds a
new kind, ManagedTransform. v0.1.0's notes announced the chart hardening as v0.1.1; it ships as a minor release
instead, because upgrading changes how Prometheus scrapes kflare (see below).

helm upgrade kflare oci://ghcr.io/jonathangraniero/charts/kflare --version 0.2.0 -n kflare-system \
  --values kflare-values.yaml

The image is ghcr.io/jonathangraniero/kflare:0.2.0, for linux/amd64 and linux/arm64. To install kflare for
the first time, follow the README's quickstart.

Upgrading from v0.1.0

Metrics are served over HTTPS, and only to callers allowed to read them. A scrape set up for v0.1.0 fails with
401 or 403 until it changes, on the same port (8080):

  • With the chart's ServiceMonitor, nothing changes in your values: it scrapes over HTTPS with Prometheus' own
    ServiceAccount token. kube-prometheus-stack's Prometheus is already allowed; any other needs its ServiceAccount
    bound to the kflare-metrics-reader ClusterRole (the deploy guide's
    Monitoring has the binding)
  • Any other scraper must use HTTPS, send a bearer token whose user is bound to kflare-metrics-reader, and either
    skip verifying the self-signed certificate or trust one from metrics.certSecret
  • To keep v0.1.0's plain HTTP, set metrics.secure: false

If you manage the CRDs yourself (crds.enabled=false), apply config/crd/bases from v0.2.0 before upgrading
the chart: it adds the ManagedTransform CRD, which the controller watches, and puts every CRD in the kflare
category.

The Helm chart

  • Metrics over HTTPS with authentication and authorization, on by default (metrics.secure). A request needs a
    bearer token that the API server authenticates and authorizes for get on /metrics, which the new
    kflare-metrics-reader ClusterRole grants. The controller's ClusterRole may create TokenReviews and
    SubjectAccessReviews to check them. metrics.certSecret serves a certificate from a Secret, such as one
    cert-manager issues, instead of a self-signed one, and the ServiceMonitor then verifies it
  • A PodDisruptionBudget with maxUnavailable: 1 when replicaCount is above 1, so that a node drain evicts one
    controller pod at a time (podDisruptionBudget). A single replica gets none, so it never blocks a drain
  • An opt-in NetworkPolicy (networkPolicy): scrapes of the metrics port in, from the sources you select; DNS, the
    Kubernetes API server and port 443 (Cloudflare) out

The controller

  • --metrics-secure also authenticates and authorizes each request now, with a TokenReview and a SubjectAccessReview;
    an allowed token is trusted for a minute. Before, it only turned on TLS
  • --metrics-cert-path serves the metrics certificate and key from a directory (tls.crt, tls.key), reloaded
    when they change
  • --default-deletion-policy, the chart's defaultDeletionPolicy: retain keeps the Cloudflare objects of deleted
    resources, unless a resource says kflare.dev/deletion-policy: delete. The default, delete, keeps v0.1.0's
    behavior (#80)
  • Every change of a resource's Ready condition to another status or reason is recorded as an Event, Normal when
    it turns True and Warning when it turns False, so kubectl describe shows the resource's history. The
    controller's ClusterRole may create and patch events.k8s.io Events
    (#56)
  • Timeouts on every call to Cloudflare, so that one that gets no answer cannot stall a controller: 10 seconds for
    a TLS handshake, a minute for a response to start, two for a whole request, and an HTTP/2 connection that goes 30
    seconds without a word is pinged and closed if the ping gets no answer. Before, a call could wait forever
    (#72)
  • --reconcile-timeout, the chart's reconcileTimeout (5 minutes by default): a reconcile that runs longer ends with
    reason APIError and is retried with back-off, and its controller goes on to the next resource
  • --drift-check-interval, the chart's driftCheckInterval, off by default: every resource is checked against
    Cloudflare that long after each successful reconcile, give or take a tenth, so that a change made outside kflare is
    corrected within about the interval instead of at the 10-hour resync. The deploy guide says how to pick one for the
    number of resources (#55)

Resource kinds

  • ManagedTransform: a zone's managed transforms, the request and response headers Cloudflare adds or removes,
    turned on and off by ID. One ManagedTransform configures a zone, and deleting it turns them all off unless it
    adopted them. It needs Zone · Managed Headers · Edit
  • Every kind is in the kflare category, so kubectl get kflare lists the resources of all of them, in one
    namespace (-n) or in all (-A) (#54)

Known limitations

Those of v0.1.0, except the two this release fixes (no Kubernetes Events, and metrics over plain HTTP).
ARCHITECTURE.md explains each:

  • Drift is corrected promptly only with a drift check interval (driftCheckInterval). Without one, the default,
    a change made outside kflare waits for the next reconcile, at worst the 10-hour resync
  • Adoption goes by key (a name, a title, a pattern), never by ID. Tunnel names are not unique: a Tunnel adopts the
    first one listed with its name
  • Ownership is recorded in the cluster, except for rules, which carry a ref in Cloudflare. A cluster rebuilt from
    git sees its existing objects as AlreadyExists until they are adopted; the deploy guide's
    Moving to a new cluster describes how
  • Rule order covers kflare's rules only. Rules created outside kflare keep their place
  • Some limits of cloudflare-go v0.119, which most controllers still use (the generated kinds and the rule kinds
    use v7): Tunnels cannot be renamed, originRequest timeouts are not exposed, and R2 buckets live in the default
    jurisdiction only

v0.1.0

Choose a tag to compare

@JonathanGraniero JonathanGraniero released this 21 Oct 17:36
6ec72bd

The first release of kflare, a Kubernetes operator that manages Cloudflare through custom resources. It is an alpha:
the API is v1alpha1, and fields can still change between releases. It needs Kubernetes 1.34 or newer.

helm install kflare oci://ghcr.io/jonathangraniero/charts/kflare \
  --version 0.1.0 --namespace kflare-system --create-namespace

The image is ghcr.io/jonathangraniero/kflare:0.1.0, for linux/amd64 and linux/arm64. Start with the
README's quickstart, and the
deploy guide for installing with Argo CD or Flux and running kflare in production.

Resource kinds

Area Kinds
Accounts CloudflareAccount (cluster-scoped; a token Secret per account)
DNS Zone, DNSRecord (tunnelRef points a hostname at a Tunnel)
Tunnels Tunnel (its token written to a Secret for cloudflared), TunnelConfiguration
Workers and storage WorkerScript, WorkerRoute, KVNamespace, R2Bucket, D1Database
Security WAFCustomRule, RateLimitRule, ManagedRuleset, IPList
Rules TransformRule, RedirectRule, CacheRule, ConfigurationRule, OriginRule

Every kind is tested live against a real Cloudflare account on the Free plan, with a token holding only the
permissions the deploy guide lists.

Adoption and deletion

  • kflare creates the Cloudflare objects its resources describe, and never takes over an existing object unless the
    resource is annotated kflare.dev/adoption-policy: adopt (adopt it, never create one) or adopt-or-create.
    Otherwise the resource reports AlreadyExists and changes nothing.
  • Deleting a resource deletes the object kflare created, and keeps an object it adopted.
    kflare.dev/deletion-policy: retain keeps any object, and delete deletes even an adopted one.
  • Deleting a KVNamespace or D1Database deletes its data. kflare never deletes objects in an R2 bucket.
  • Resources can be deleted in any order, a whole namespace included: parents wait for the resources that need them.

See Adoption and ownership and Deletion.

The Helm chart

  • Installs and upgrades the CRDs as templates, and keeps them on uninstall (helm.sh/resource-policy: keep)
  • Runs the controller under the restricted Pod Security Standard, with leader election, so extra replicas stand by
  • Adds kflare's namespaced kinds to the built-in edit and admin roles, and every kind to view;
    CloudflareAccount is left to cluster administrators
  • Serves Prometheus metrics, with an optional ServiceMonitor; values.schema.json validates the values

Known limitations

  • Drift is not corrected promptly. A change made outside kflare is corrected on the next reconcile, which can be up
    to 10 hours away. Changing the resource, even an annotation, reconciles it straight away.
  • Adoption goes by key (a name, a title, a pattern), never by ID. Tunnel names are not unique: a Tunnel adopts the
    first one listed with its name.
  • Ownership is recorded in the cluster, except for rules, which carry a ref in Cloudflare. A cluster rebuilt from
    git sees its existing objects as AlreadyExists until they are adopted; the deploy guide's
    Moving to a new cluster describes how.
  • Rule order covers kflare's rules only. Rules created outside kflare keep their place.
  • Some limits of cloudflare-go v0.119, which most controllers still use: Tunnels cannot be renamed, originRequest
    timeouts are not exposed, and R2 buckets live in the default jurisdiction only.
  • No Kubernetes Events yet: the Ready condition and the controller's log say what happened.
  • Metrics are served over plain HTTP without authentication, inside the cluster.

Coming in v0.1.1

The chart will serve metrics over HTTPS with authentication and authorization by default, which changes how
Prometheus scrapes kflare. It will also create a PodDisruptionBudget when replicaCount is above 1, and gain an opt-in
NetworkPolicy. The release notes will say what to change.