feat(cloudflare-tunnel): add optional cloudflared connector module - #92
Conversation
📝 WalkthroughWalkthroughAdds a new Terraform module for deploying a Cloudflare Tunnel connector to Kubernetes, wires it into the stage2 configuration with new variables and README documentation, and includes unrelated maintenance changes: bitnami-labs to bitnami URL updates, Dockerfile version bumps, and a refreshed trivy ignore list. ChangesCloudflare Tunnel Module
Unrelated maintenance updates
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Terraform
participant Kubernetes
participant HelmRelease as cloudflare-tunnel-remote
Terraform->>Kubernetes: create namespace cloudflare-tunnel
Terraform->>HelmRelease: install chart with set_sensitive tunnel_token
Terraform->>HelmRelease: set replica_count
Terraform->>HelmRelease: conditionally set image.tag
HelmRelease->>Kubernetes: deploy connector pods
Related Issues: None specified in the provided information. Related PRs: None specified in the provided information. Suggested labels: terraform, documentation, dependencies Suggested reviewers: chrisleekr 🐰 A tunnel dug beneath the net, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.trivyignore.yaml:
- Around line 4-73: The ignores in .trivyignore.yaml are currently image-wide,
but this set should be narrowed to the affected artifacts only. Update each
vulnerability entry to use the supported paths and/or purls fields so the CVEs
are scoped to the specific binaries/packages mentioned in the existing
statements (kubectl, go-task, terraform, tflint, and rekor) instead of applying
globally. Keep the same CVE ids and expiry data, but attach the ignore rules to
the matching artifact identifiers so unrelated future occurrences are still
reported.
In `@stage2/cloudflare-tunnel/main.tf`:
- Around line 27-59: The cloudflare_tunnel helm_release allows an empty
cloudflare_tunnel_token, which can let deployment succeed while the tunnel later
crash-loops. Add a lifecycle.precondition on the helm_release
"cloudflare_tunnel" resource that checks var.cloudflare_tunnel_token is
non-empty when the module is enabled, and surface a clear error message so
Terraform fails fast before applying. Use the existing cloudflare_tunnel_token
input and cloudflare_tunnel resource to keep the guard self-contained.
In `@stage2/cloudflare-tunnel/variables.tf`:
- Around line 18-22: Add validation to the cloudflare_tunnel_replica_count
variable so it cannot be set to 0, since that value would disable the tunnel
connector. Update the variable block in variables.tf to enforce a minimum of 1,
and keep the constraint aligned with how cloudflare_tunnel_replica_count is
passed through stage2/cloudflare-tunnel/main.tf into the Helm chart
replicaCount.
In `@stage2/variables.tf`:
- Around line 802-831: Add validation blocks to the new Cloudflare Tunnel
variables in variables.tf to match the stage2 variable guidelines. Update each
of cloudflare_tunnel_enable, cloudflare_tunnel_token,
cloudflare_tunnel_chart_version, cloudflare_tunnel_image_tag, and
cloudflare_tunnel_replica_count with appropriate validation, and make sure
cloudflare_tunnel_chart_version rejects empty strings while
cloudflare_tunnel_replica_count requires a value of at least 1. Keep the checks
close to the variable definitions so they’re easy to find and maintain.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b7c4b811-c695-4c43-91ac-d7dfba00bb07
📒 Files selected for processing (15)
.env.sample.trivyignore.yaml.vscode/settings.jsonDockerfileREADME.mdstage2/bitnami-sealed-secrets/README.mdstage2/bitnami-sealed-secrets/sealed-secrets.tfstage2/bitnami-sealed-secrets/templates/sealed-secrets-values.tftplstage2/cloudflare-tunnel/.terraform.lock.hclstage2/cloudflare-tunnel/README.mdstage2/cloudflare-tunnel/main.tfstage2/cloudflare-tunnel/provider.tfstage2/cloudflare-tunnel/variables.tfstage2/main.tfstage2/variables.tf
| # | ||
| # Every vulnerability below is a HIGH CVE in a Go dependency vendored by an upstream CLI | ||
| # tool (kubectl, go-task, terraform, tflint). All four tools are already pinned to their | ||
| # latest stable release in the Dockerfile, so these can only clear when the upstream | ||
| # project rebuilds with the fixed dependency. Verified against a local image scan on | ||
| # 2026-07-02 (trivy 0.72.0): this is the complete HIGH/CRITICAL set, nothing else remains. | ||
| # Re-evaluate on expired_at and drop each entry once its tool ships a fixed release. | ||
|
|
||
| vulnerabilities: | ||
| # Go stdlib vulnerabilities in upstream tools (helm, kubectl, terraform, tflint, trivy) | ||
| # These require upstream maintainers to rebuild with Go 1.24.12+ or 1.25.6+ | ||
| - id: CVE-2025-61726 | ||
| statement: Go stdlib net/url - no limit on query parameters - waiting for upstream rebuild | ||
| - id: CVE-2025-61728 | ||
| statement: Go stdlib archive/zip - excessive CPU consumption - waiting for upstream rebuild | ||
| - id: CVE-2025-61729 | ||
| statement: Go stdlib crypto/x509 in tflint - waiting for upstream fix | ||
| - id: CVE-2025-66564 | ||
| statement: sigstore/timestamp-authority in tflint/trivy - waiting for upstream fix | ||
| - id: CVE-2026-25679 | ||
| statement: net/url Incorrect parsing of IPv6 host | ||
| - id: CVE-2025-15558 | ||
| statement: docker/cli Docker CLI for Windows Privilege malicious plugin binaries | ||
| - id: CVE-2026-33186 | ||
| statement: gRPC-Go has an authorization bypass via missing slash in path | ||
| - id: CVE-2026-24051 | ||
| statement: OpenTelemetry Go SDK Vulnerable to Arbitrary via PATH Hijacking | ||
| - id: CVE-2026-32280 | ||
| statement: Go has Denial of Service vulnerability in certificate chain building | ||
| - id: CVE-2026-32281 | ||
| statement: Go has Denial of Service vulnerability via inefficient certificate chain validation | ||
| - id: CVE-2026-32283 | ||
| statement: Go has Denial of Service vulnerability via multiple TLS 1.3 key | ||
| # Go stdlib crypto/tls vulnerabilities in all upstream Go binaries | ||
| # (helm, kubectl, terraform, task, tflint, trivy) | ||
| # NVD CVSS 10.0 (CRITICAL), CISA-ADP 4.8 (MEDIUM) — risk mitigated: CI container not exposed to untrusted TLS | ||
| # Requires Go >= 1.25.7 - no upstream tool has been rebuilt yet as of 2026-02-20 | ||
| # Reference: https://security.snyk.io/vuln/SNYK-GOLANG-STDCRYPTOTLS-15238826 | ||
| # TODO(CVE-2025-68121): Remove once upstream tools (helm, kubectl, terraform, tflint, trivy) rebuild with Go >= 1.25.7 | ||
| - id: CVE-2025-68121 | ||
| statement: Go stdlib crypto/tls session resumption - waiting for upstream rebuild with Go 1.25.7+ | ||
| # NVD CVSS pending, Ubuntu rates MEDIUM — Requires Go >= 1.25.6 | ||
| # Fixed in trivy v0.69.1, other tools still pending upstream rebuild | ||
| # Reference: https://avd.aquasec.com/nvd/cve-2025-61730 | ||
| # TODO(CVE-2025-61730): Remove once upstream tools (helm, kubectl, terraform, tflint) rebuild with Go >= 1.25.6 | ||
| - id: CVE-2025-61730 | ||
| statement: Go stdlib crypto/tls TLS 1.3 handshake - waiting for upstream rebuild with Go 1.25.6+ | ||
| # Alpine transitive dependencies - waiting for base image updates | ||
| - id: CVE-2026-22695 | ||
| statement: libpng transitive dependency via graphviz | ||
| - id: CVE-2026-22801 | ||
| statement: libpng transitive dependency via graphviz | ||
| # Go stdlib vulnerabilities affecting kubectl/task/terraform/trivy binaries | ||
| # Fix landed in Go 1.25.10 / 1.26.3; kubectl 1.36.1 ships Go 1.26.2, task 3.51.1 | ||
| # and terraform 1.15.4 still on older Go. trivy 0.70.0 is latest but pre-Go-1.26.3. | ||
| # Re-evaluate after upstream rebuilds (~30 days). | ||
| # TODO: Remove once kubectl >= 1.36.2 and trivy >= 0.71.0 (or equivalent rebuilds) land. | ||
| - id: CVE-2026-33811 | ||
| statement: Go stdlib LookupCNAME cgo DNS resolver - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| # golang.org/x/net HIGH CVEs vendored by kubectl 1.36.2 (x/net v0.49.0) and | ||
| # go-task 3.51.1 (x/net v0.54.0). Fixed in x/net 0.53.0-0.55.0. | ||
| # Unblocks when kubectl and go-task rebuild with x/net >= 0.55.0. | ||
| - id: CVE-2026-25681 | ||
| statement: golang.org/x/net/html arbitrary code execution via XSS (kubectl/task) - waiting for upstream rebuild with x/net >= 0.55.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-27136 | ||
| statement: golang.org/x/net/html Render XSS (kubectl/task) - waiting for upstream rebuild with x/net >= 0.55.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-33814 | ||
| statement: Go stdlib HTTP/2 SETTINGS frames infinite loop - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-39820 | ||
| statement: Go stdlib net/mail ParseAddress malformed input - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-39836 | ||
| statement: Go stdlib net.Dial/LookupPort NUL byte panic (Windows-only, low risk on Alpine) - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-42499 | ||
| statement: Go stdlib net/mail consumePhrase DoS - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-39823 | ||
| statement: Go stdlib net/url parsing in kubectl/trivy - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-39825 | ||
| statement: Go stdlib net/http/httputil ReverseProxy query forwarding in kubectl/trivy - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-39826 | ||
| statement: Go stdlib html/template script tag escaping in kubectl/trivy - waiting for upstream rebuild with Go 1.25.10+/1.26.3+ | ||
| expired_at: 2026-06-22 | ||
| # Trivy 0.70.0 transitive dependencies - fixed upstream, awaiting next Trivy release | ||
| - id: CVE-2026-46680 | ||
| statement: containerd user ID handling bypass in trivy v0.70.0 (vendored v1.7.30/v2.2.2) - fixed in containerd 1.7.32/2.2.4, awaiting trivy rebuild | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-45022 | ||
| statement: go-git parsing flaw in trivy v0.70.0 (vendored v5.17.2) - fixed in go-git 5.19.0, awaiting trivy rebuild | ||
| expired_at: 2026-06-22 | ||
| - id: CVE-2026-44973 | ||
| statement: go-billy path traversal in trivy v0.70.0 (vendored v5.8.0) - fixed in go-billy 5.9.0, awaiting trivy rebuild | ||
| expired_at: 2026-06-22 | ||
| statement: golang.org/x/net HTTP/2 SETTINGS_MAX_FRAME_SIZE DoS (kubectl) - waiting for upstream rebuild with x/net >= 0.53.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39821 | ||
| statement: golang.org/x/net/idna privilege escalation via Punycode label processing (kubectl/task) - waiting for upstream rebuild with x/net >= 0.55.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-42502 | ||
| statement: golang.org/x/net/html Render XSS (kubectl/task) - waiting for upstream rebuild with x/net >= 0.55.0 | ||
| expired_at: 2026-08-15 | ||
|
|
||
| # golang.org/x/crypto SSH HIGH CVEs vendored by go-task 3.51.1 (x/crypto v0.51.0). | ||
| # Fixed in x/crypto 0.52.0. go-task main already bumped to 0.53.0; unblocks on next task release. | ||
| - id: CVE-2026-39827 | ||
| statement: golang.org/x/crypto/ssh DoS via repeatedly opened channels (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39828 | ||
| statement: golang.org/x/crypto/ssh HIGH (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39829 | ||
| statement: golang.org/x/crypto/ssh HIGH (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39830 | ||
| statement: golang.org/x/crypto/ssh DoS via resource leak from unsolicited SSH responses (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39832 | ||
| statement: golang.org/x/crypto/ssh/agent security bypass via improper key restriction handling (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-39835 | ||
| statement: golang.org/x/crypto/ssh DoS via crafted SSH certificate (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-42508 | ||
| statement: golang.org/x/crypto/ssh/knownhosts revocation bypass via unchecked SignatureKey (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-46595 | ||
| statement: golang.org/x/crypto/ssh authorization bypass via skipped source-address validation (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
| - id: CVE-2026-46597 | ||
| statement: golang.org/x/crypto/ssh server-side panic via incorrectly placed bytes-to-int cast (task) - waiting for upstream rebuild with x/crypto >= 0.52.0 | ||
| expired_at: 2026-08-15 | ||
|
|
||
| # Go stdlib crypto/x509 DoS. Fixed in Go 1.25.11 / 1.26.4. Present in terraform 1.15.7 | ||
| # (Go 1.25.10), go-task 3.51.1 (Go 1.26.3), tflint 0.63.1 (Go 1.26.3). | ||
| # Unblocks when each tool rebuilds with Go >= 1.25.11 / 1.26.4. | ||
| - id: CVE-2026-27145 | ||
| statement: Go stdlib crypto/x509 DoS via excessive DNS name constraint processing (terraform/task/tflint) - waiting for upstream rebuild with Go >= 1.25.11/1.26.4 | ||
| expired_at: 2026-08-15 | ||
|
|
||
| # github.com/sigstore/rekor OOM via unbounded gzip, vendored by tflint 0.63.1 (rekor v1.5.0). | ||
| # Fixed in rekor 1.5.2. Unblocks on next tflint release bumping rekor. | ||
| - id: CVE-2026-48702 | ||
| statement: github.com/sigstore/rekor OOM via unbounded gzip (tflint) - waiting for upstream rebuild with rekor >= 1.5.2 | ||
| expired_at: 2026-08-15 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Spot-check a sample of the listed CVE IDs against NVD/GHSA advisories
for id in CVE-2026-25681 CVE-2026-39827 CVE-2026-27145 CVE-2026-48702; do
echo "== $id =="
curl -s "https://api.osv.dev/v1/vulns/$id" | jq '.summary, .affected[].ranges'
doneRepository: chrisleekr/homelab-infrastructure
Length of output: 507
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== trivy ignore references =="
git ls-files | rg '(^|/)(\.trivyignore\.ya?ml|container-security\.yml|trivy|security)' || true
echo
echo "== .trivyignore.yaml =="
if git ls-files --error-unmatch .trivyignore.yaml >/dev/null 2>&1; then
cat -n .trivyignore.yaml
fi
echo
echo "== CI workflow / job references =="
rg -n --hidden --no-ignore-vcs 'trivy image|--ignorefile|--exit-code 1|--format sarif|\.trivyignore\.ya?ml' .github . 2>/dev/null || true
echo
echo "== trivyignore docs / examples in repo =="
rg -n --hidden --no-ignore-vcs 'paths:|pkgs:|statement:|expired_at:|trivyignore' . 2>/dev/null || trueRepository: chrisleekr/homelab-infrastructure
Length of output: 13579
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== trivy ignore schema references =="
rg -n --hidden --no-ignore-vcs 'paths:|pkgs:|vulnerabilities:|misconfigurations:|statement:|expired_at:' . \
--glob '*.yml' --glob '*.yaml' --glob '*.md' 2>/dev/null || true
echo
echo "== artifact version pins =="
rg -n --hidden --no-ignore-vcs 'kubectl|go-task|terraform|tflint|FROM .*?(kubectl|task|terraform|tflint)|version:' . \
--glob 'Dockerfile*' --glob '*.yml' --glob '*.yaml' --glob 'Taskfile.yml' --glob '.github/workflows/*.yml' \
--glob '.gitlab-ci.yml' 2>/dev/null || true
echo
echo "== trivy docs in repo =="
sed -n '1,140p' .pre-commit-config.yaml 2>/dev/null || trueRepository: chrisleekr/homelab-infrastructure
Length of output: 31286
🌐 Web query:
Trivy .trivyignore.yaml vulnerability ignore schema paths pkgs package scoping documentation
💡 Result:
The.trivyignore.yaml file is a configuration file used by Trivy to filter out specific findings (vulnerabilities, misconfigurations, secrets, and licenses) from scan reports [1][2]. Structure and Schema While there is no single official JSON schema document published for.trivyignore.yaml, its structure is defined by the Trivy configuration loader [3][4]. The file is structured by finding type, with each category containing a list of objects specifying filters [1][2]. Common fields for entries include [1][2]: - id: The identifier of the vulnerability, misconfiguration, secret, or license. - paths: A list of file paths (as strings) to which the ignore rule should be applied. If omitted, the rule applies to all files. - purls: A list of Package URLs (PURLs) to ignore. This field is specifically available for vulnerabilities. - expired_at: A date (yyyy-mm-dd) after which the ignore rule is no longer valid. - statement: A field used to record the reasoning for the ignore rule (not used for filtering). As of recent updates, the id field can be omitted in some contexts, allowing rules to be scoped purely by paths or purls [5]. Usage and Scoping - Usage: Because this feature is experimental, the.trivyignore.yaml file must be explicitly loaded using the --ignorefile flag (e.g., trivy repo --ignorefile.trivyignore.yaml.) [1][6]. - Path Scoping: The paths field is used to target specific files. For OS packages, note that this may not work as expected; for projects with dependency files (e.g., go.mod, yarn.lock), the path should point to the specific dependency file [2]. - Package Scoping: PURLs allow for precise scoping of vulnerabilities to specific packages (e.g., pkg:deb/debian/libssl1.1) [1][2]. Example Structure vulnerabilities: - id: CVE-2022-40897 paths: - "usr/local/lib/python3.9/site-packages/setuptools-58.1.0.dist-info/METADATA" statement: "Accept the risk" - id: CVE-2023-3817 purls: - "pkg:deb/debian/libssl1.1" misconfigurations: - id: AVD-DS-0001 paths: - "docs/Dockerfile"
Citations:
- 1: https://trivy.dev/docs/latest/configuration/filtering/
- 2: https://github.com/aquasecurity/trivy/blob/main/docs/guide/configuration/filtering.md
- 3: https://github.com/aquasecurity/trivy/blob/main/pkg/result/ignore.go
- 4: Add JSON Schema for `.trivyignore.yaml` configuration file aquasecurity/trivy#10424
- 5: feat(ignore): make id optional in .trivyignore.yaml to ignore all findings for a PURL/path aquasecurity/trivy#10583
- 6: feat: add support for .trivyignore.yaml aquasecurity/trivy#5070
🌐 Web query:
CVE-2026-25681 CVE-2026-39827 CVE-2026-27145 CVE-2026-48702 advisory fixed versions x/net x/crypto Go rekor
💡 Result:
The vulnerabilities requested have the following fixed versions and details: CVE-2026-25681: Affects golang.org/x/net (specifically golang.org/x/net/html) [1]. It is fixed in version 0.55.0 [1]. The vulnerability involves improper handling of arbitrary HTML input leading to potential XSS [1]. CVE-2026-39827: Affects golang.org/x/crypto (specifically golang.org/x/crypto/ssh) [2]. It is fixed in version 0.52.0 [2]. The vulnerability involves unbounded memory growth in the SSH server when handling rejected channels [2]. CVE-2026-27145: Affects the Go standard library (crypto/x509) [3]. It is fixed in versions 1.25.11 and 1.26.4 [3]. The vulnerability is a performance issue in (*x509.Certificate).VerifyHostname causing quadratic scaling during DNS SAN verification [3]. CVE-2026-48702: Affects sigstore/rekor [4]. It is fixed in version 1.5.2 [4]. The vulnerability involves an out-of-memory (OOM) condition caused by unbounded gzip decompression when parsing Alpine APK files [4]. Summary of Fixed Versions: - golang.org/x/net: 0.55.0 [1] - golang.org/x/crypto: 0.52.0 [2] - Go standard library (crypto/x509): 1.25.11 or 1.26.4 [3] - sigstore/rekor: 1.5.2 [4]
Citations:
- 1: https://nvd.nist.gov/vuln/detail/CVE-2026-25681
- 2: https://nvd.nist.gov/vuln/detail/CVE-2026-39827
- 3: https://nvd.nist.gov/vuln/detail/CVE-2026-27145
- 4: GHSA-47q9-m4ww-924m
Scope the vulnerability ignores to specific artifacts .trivyignore.yaml supports paths and purls, so these CVEs can be narrowed to the affected binary/package instead of being ignored image-wide. That avoids hiding the same CVE ID if it shows up in a different component later.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.trivyignore.yaml around lines 4 - 73, The ignores in .trivyignore.yaml are
currently image-wide, but this set should be narrowed to the affected artifacts
only. Update each vulnerability entry to use the supported paths and/or purls
fields so the CVEs are scoped to the specific binaries/packages mentioned in the
existing statements (kubectl, go-task, terraform, tflint, and rekor) instead of
applying globally. Keep the same CVE ids and expiry data, but attach the ignore
rules to the matching artifact identifiers so unrelated future occurrences are
still reported.
| resource "helm_release" "cloudflare_tunnel" { | ||
| depends_on = [kubernetes_namespace_v1.cloudflare_tunnel] | ||
|
|
||
| name = "cloudflare-tunnel" | ||
| repository = "https://cloudflare.github.io/helm-charts" | ||
| chart = "cloudflare-tunnel-remote" | ||
| version = var.cloudflare_tunnel_chart_version | ||
| namespace = kubernetes_namespace_v1.cloudflare_tunnel.metadata[0].name | ||
| timeout = 300 | ||
| wait = true | ||
|
|
||
| set_sensitive = [ | ||
| { | ||
| name = "cloudflare.tunnel_token" | ||
| value = var.cloudflare_tunnel_token | ||
| } | ||
| ] | ||
|
|
||
| set = concat( | ||
| [ | ||
| { | ||
| name = "replicaCount" | ||
| value = tostring(var.cloudflare_tunnel_replica_count) | ||
| } | ||
| ], | ||
| var.cloudflare_tunnel_image_tag != "" ? [ | ||
| { | ||
| name = "image.tag" | ||
| value = var.cloudflare_tunnel_image_tag | ||
| } | ||
| ] : [] | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
Fail fast when the token is empty instead of deploying a broken tunnel.
var.cloudflare_tunnel_token defaults to "" in stage2/variables.tf with no cross-variable validation enforcing it when the module is enabled. If a user sets cloudflare_tunnel_enable = true without providing a token, this helm_release deploys successfully but cloudflared will crash-loop with an unclear error. A lifecycle.precondition here is a self-contained, version-agnostic guard (Terraform ≥1.2) that doesn't require cross-variable validation support.
🛡️ Proposed fix
resource "helm_release" "cloudflare_tunnel" {
depends_on = [kubernetes_namespace_v1.cloudflare_tunnel]
name = "cloudflare-tunnel"
repository = "https://cloudflare.github.io/helm-charts"
chart = "cloudflare-tunnel-remote"
version = var.cloudflare_tunnel_chart_version
namespace = kubernetes_namespace_v1.cloudflare_tunnel.metadata[0].name
timeout = 300
wait = true
+
+ lifecycle {
+ precondition {
+ condition = var.cloudflare_tunnel_token != ""
+ error_message = "cloudflare_tunnel_token must be set when the cloudflare-tunnel module is enabled."
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| resource "helm_release" "cloudflare_tunnel" { | |
| depends_on = [kubernetes_namespace_v1.cloudflare_tunnel] | |
| name = "cloudflare-tunnel" | |
| repository = "https://cloudflare.github.io/helm-charts" | |
| chart = "cloudflare-tunnel-remote" | |
| version = var.cloudflare_tunnel_chart_version | |
| namespace = kubernetes_namespace_v1.cloudflare_tunnel.metadata[0].name | |
| timeout = 300 | |
| wait = true | |
| set_sensitive = [ | |
| { | |
| name = "cloudflare.tunnel_token" | |
| value = var.cloudflare_tunnel_token | |
| } | |
| ] | |
| set = concat( | |
| [ | |
| { | |
| name = "replicaCount" | |
| value = tostring(var.cloudflare_tunnel_replica_count) | |
| } | |
| ], | |
| var.cloudflare_tunnel_image_tag != "" ? [ | |
| { | |
| name = "image.tag" | |
| value = var.cloudflare_tunnel_image_tag | |
| } | |
| ] : [] | |
| ) | |
| } | |
| resource "helm_release" "cloudflare_tunnel" { | |
| depends_on = [kubernetes_namespace_v1.cloudflare_tunnel] | |
| name = "cloudflare-tunnel" | |
| repository = "https://cloudflare.github.io/helm-charts" | |
| chart = "cloudflare-tunnel-remote" | |
| version = var.cloudflare_tunnel_chart_version | |
| namespace = kubernetes_namespace_v1.cloudflare_tunnel.metadata[0].name | |
| timeout = 300 | |
| wait = true | |
| lifecycle { | |
| precondition { | |
| condition = var.cloudflare_tunnel_token != "" | |
| error_message = "cloudflare_tunnel_token must be set when the cloudflare-tunnel module is enabled." | |
| } | |
| } | |
| set_sensitive = [ | |
| { | |
| name = "cloudflare.tunnel_token" | |
| value = var.cloudflare_tunnel_token | |
| } | |
| ] | |
| set = concat( | |
| [ | |
| { | |
| name = "replicaCount" | |
| value = tostring(var.cloudflare_tunnel_replica_count) | |
| } | |
| ], | |
| var.cloudflare_tunnel_image_tag != "" ? [ | |
| { | |
| name = "image.tag" | |
| value = var.cloudflare_tunnel_image_tag | |
| } | |
| ] : [] | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stage2/cloudflare-tunnel/main.tf` around lines 27 - 59, The cloudflare_tunnel
helm_release allows an empty cloudflare_tunnel_token, which can let deployment
succeed while the tunnel later crash-loops. Add a lifecycle.precondition on the
helm_release "cloudflare_tunnel" resource that checks
var.cloudflare_tunnel_token is non-empty when the module is enabled, and surface
a clear error message so Terraform fails fast before applying. Use the existing
cloudflare_tunnel_token input and cloudflare_tunnel resource to keep the guard
self-contained.
| variable "cloudflare_tunnel_replica_count" { | ||
| description = "Number of cloudflared replicas. HA only, do NOT autoscale (downscaling breaks live connections)." | ||
| type = number | ||
| default = 2 | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
Add validation to prevent replica_count of 0.
cloudflare_tunnel_replica_count feeds directly into the Helm chart's replicaCount (per stage2/cloudflare-tunnel/main.tf). A value of 0 would silently disable the tunnel connector, contradicting the "HA only" intent documented in the description.
♻️ Proposed validation
variable "cloudflare_tunnel_replica_count" {
description = "Number of cloudflared replicas. HA only, do NOT autoscale (downscaling breaks live connections)."
type = number
default = 2
+
+ validation {
+ condition = var.cloudflare_tunnel_replica_count >= 1
+ error_message = "cloudflare_tunnel_replica_count must be at least 1."
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| variable "cloudflare_tunnel_replica_count" { | |
| description = "Number of cloudflared replicas. HA only, do NOT autoscale (downscaling breaks live connections)." | |
| type = number | |
| default = 2 | |
| } | |
| variable "cloudflare_tunnel_replica_count" { | |
| description = "Number of cloudflared replicas. HA only, do NOT autoscale (downscaling breaks live connections)." | |
| type = number | |
| default = 2 | |
| validation { | |
| condition = var.cloudflare_tunnel_replica_count >= 1 | |
| error_message = "cloudflare_tunnel_replica_count must be at least 1." | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stage2/cloudflare-tunnel/variables.tf` around lines 18 - 22, Add validation
to the cloudflare_tunnel_replica_count variable so it cannot be set to 0, since
that value would disable the tunnel connector. Update the variable block in
variables.tf to enforce a minimum of 1, and keep the constraint aligned with how
cloudflare_tunnel_replica_count is passed through
stage2/cloudflare-tunnel/main.tf into the Helm chart replicaCount.
| variable "cloudflare_tunnel_enable" { | ||
| description = "Deploy the remotely-managed Cloudflare Tunnel connector (cloudflared) into the cluster." | ||
| type = bool | ||
| default = false | ||
| } | ||
|
|
||
| variable "cloudflare_tunnel_token" { | ||
| description = "Cloudflare Tunnel token (sensitive). Not self-generated; Cloudflare issues it per tunnel. Get it: dashboard > Zero Trust > Networks > Tunnels > Create a tunnel > cloudflared > name it > Save; on the install screen copy the value after --token (the eyJ... string)." | ||
| type = string | ||
| sensitive = true | ||
| default = "" | ||
| } | ||
|
|
||
| variable "cloudflare_tunnel_chart_version" { | ||
| description = "cloudflare-tunnel-remote Helm chart version. Pinned per repo convention. Reference: helm show chart cloudflare/cloudflare-tunnel-remote" | ||
| type = string | ||
| default = "0.1.2" # empty would resolve to latest chart and defeat pinning | ||
| } | ||
|
|
||
| variable "cloudflare_tunnel_image_tag" { | ||
| description = "cloudflared image tag to pin. Empty uses the chart default. Reference: https://github.com/cloudflare/cloudflared/releases" | ||
| type = string | ||
| default = "" | ||
| } | ||
|
|
||
| variable "cloudflare_tunnel_replica_count" { | ||
| description = "Number of cloudflared replicas. HA only; do not autoscale (downscaling breaks live connections)." | ||
| type = number | ||
| default = 2 | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Add validation blocks to the new variables.
None of the five new variables (cloudflare_tunnel_enable, cloudflare_tunnel_token, cloudflare_tunnel_chart_version, cloudflare_tunnel_image_tag, cloudflare_tunnel_replica_count) include a validation block. As per coding guidelines, stage2/variables.tf should "Include type, description, default, and validation when adding variables to stage2/variables.tf". At minimum, cloudflare_tunnel_chart_version should reject empty strings (an empty version defeats pinning, per its own description) and cloudflare_tunnel_replica_count should enforce >= 1.
♻️ Example validation additions
variable "cloudflare_tunnel_chart_version" {
description = "cloudflare-tunnel-remote Helm chart version. Pinned per repo convention. Reference: helm show chart cloudflare/cloudflare-tunnel-remote"
type = string
default = "0.1.2" # empty would resolve to latest chart and defeat pinning
+
+ validation {
+ condition = length(trimspace(var.cloudflare_tunnel_chart_version)) > 0
+ error_message = "cloudflare_tunnel_chart_version must not be empty."
+ }
}
variable "cloudflare_tunnel_replica_count" {
description = "Number of cloudflared replicas. HA only; do not autoscale (downscaling breaks live connections)."
type = number
default = 2
+
+ validation {
+ condition = var.cloudflare_tunnel_replica_count >= 1
+ error_message = "cloudflare_tunnel_replica_count must be at least 1."
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| variable "cloudflare_tunnel_enable" { | |
| description = "Deploy the remotely-managed Cloudflare Tunnel connector (cloudflared) into the cluster." | |
| type = bool | |
| default = false | |
| } | |
| variable "cloudflare_tunnel_token" { | |
| description = "Cloudflare Tunnel token (sensitive). Not self-generated; Cloudflare issues it per tunnel. Get it: dashboard > Zero Trust > Networks > Tunnels > Create a tunnel > cloudflared > name it > Save; on the install screen copy the value after --token (the eyJ... string)." | |
| type = string | |
| sensitive = true | |
| default = "" | |
| } | |
| variable "cloudflare_tunnel_chart_version" { | |
| description = "cloudflare-tunnel-remote Helm chart version. Pinned per repo convention. Reference: helm show chart cloudflare/cloudflare-tunnel-remote" | |
| type = string | |
| default = "0.1.2" # empty would resolve to latest chart and defeat pinning | |
| } | |
| variable "cloudflare_tunnel_image_tag" { | |
| description = "cloudflared image tag to pin. Empty uses the chart default. Reference: https://github.com/cloudflare/cloudflared/releases" | |
| type = string | |
| default = "" | |
| } | |
| variable "cloudflare_tunnel_replica_count" { | |
| description = "Number of cloudflared replicas. HA only; do not autoscale (downscaling breaks live connections)." | |
| type = number | |
| default = 2 | |
| } | |
| variable "cloudflare_tunnel_enable" { | |
| description = "Deploy the remotely-managed Cloudflare Tunnel connector (cloudflared) into the cluster." | |
| type = bool | |
| default = false | |
| } | |
| variable "cloudflare_tunnel_token" { | |
| description = "Cloudflare Tunnel token (sensitive). Not self-generated; Cloudflare issues it per tunnel. Get it: dashboard > Zero Trust > Networks > Tunnels > Create a tunnel > cloudflared > name it > Save; on the install screen copy the value after --token (the eyJ... string)." | |
| type = string | |
| sensitive = true | |
| default = "" | |
| } | |
| variable "cloudflare_tunnel_chart_version" { | |
| description = "cloudflare-tunnel-remote Helm chart version. Pinned per repo convention. Reference: helm show chart cloudflare/cloudflare-tunnel-remote" | |
| type = string | |
| default = "0.1.2" # empty would resolve to latest chart and defeat pinning | |
| validation { | |
| condition = length(trimspace(var.cloudflare_tunnel_chart_version)) > 0 | |
| error_message = "cloudflare_tunnel_chart_version must not be empty." | |
| } | |
| } | |
| variable "cloudflare_tunnel_image_tag" { | |
| description = "cloudflared image tag to pin. Empty uses the chart default. Reference: https://github.com/cloudflare/cloudflared/releases" | |
| type = string | |
| default = "" | |
| } | |
| variable "cloudflare_tunnel_replica_count" { | |
| description = "Number of cloudflared replicas. HA only; do not autoscale (downscaling breaks live connections)." | |
| type = number | |
| default = 2 | |
| validation { | |
| condition = var.cloudflare_tunnel_replica_count >= 1 | |
| error_message = "cloudflare_tunnel_replica_count must be at least 1." | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@stage2/variables.tf` around lines 802 - 831, Add validation blocks to the new
Cloudflare Tunnel variables in variables.tf to match the stage2 variable
guidelines. Update each of cloudflare_tunnel_enable, cloudflare_tunnel_token,
cloudflare_tunnel_chart_version, cloudflare_tunnel_image_tag, and
cloudflare_tunnel_replica_count with appropriate validation, and make sure
cloudflare_tunnel_chart_version rejects empty strings while
cloudflare_tunnel_replica_count requires a value of at least 1. Keep the checks
close to the variable definitions so they’re easy to find and maintain.
Source: Coding guidelines
Summary
Adds an optional Terraform module that deploys a remotely-managed Cloudflare Tunnel connector (
cloudflared), letting services be exposed through Cloudflare without opening inbound ports or relying on public IP/CGNAT traversal. Also fixes a stalebitnami-labsGitHub org reference in the sealed-secrets module that now 404s.What changed
New:
stage2/cloudflare-tunnel/modulemain.tf: creates acloudflare-tunnelnamespace and ahelm_releasefor chartcloudflare-tunnel-remotefromhttps://cloudflare.github.io/helm-charts. Tunnel token passed viaset_sensitive; replica count and optionalimage.tagviaset.provider.tf: requireshashicorp/kubernetes ~> 3.0andhashicorp/helm ~> 3.1, Terraform>= 1.5.0.variables.tf:cloudflare_tunnel_token(sensitive, no default),cloudflare_tunnel_chart_version,cloudflare_tunnel_image_tag(default""= chart default),cloudflare_tunnel_replica_count(default2, HA-only, not meant to autoscale).README.md: module docs (scope, requirements, how to obtain the tunnel token).Wiring
stage2/main.tf: newmodule "cloudflare_tunnel"gated bycount = var.cloudflare_tunnel_enable ? 1 : 0, withdepends_on = [module.nginx](tunnel's catch-all hostname points at the ingress-nginx service).stage2/variables.tf: root-levelcloudflare_tunnel_enable(defaultfalse),cloudflare_tunnel_token,cloudflare_tunnel_chart_version(default"0.1.2"),cloudflare_tunnel_image_tag,cloudflare_tunnel_replica_count(default2)..env.sample: documents the newTF_VAR_cloudflare_tunnel_*variables and how to get a tunnel token from the Cloudflare Zero Trust dashboard..vscode/settings.json: addedcloudflaredto the spellcheck word list.README.md: added module table row and optional-module toggle row.Fix:
stage2/bitnami-sealed-secrets/bitnami-labstobitnami; the old Helm repohttps://bitnami-labs.github.io/sealed-secretsnow 404s.repositoryURL insealed-secrets.tftohttps://bitnami.github.io/sealed-secrets.bitnami-labs/sealed-secretsGitHub links inREADME.mdand the values template comment insealed-secrets-values.tftpltobitnami/sealed-secrets.2.18.1/0.35.0).How to enable
eyJ...value after--token)..env:https://nginx-ingress-nginx-controller.nginx.svc:443, No TLS Verify) in the Cloudflare dashboard; ingress-nginx continues to route by Host header as usual.Summary by CodeRabbit
New Features
Documentation
Bug Fixes