Skip to content

Commit 517f855

Browse files
Merge pull request #18 from disentangle-network/feature/cloudflare-tunnel
feat: add cloudflare-tunnel chart for zero-trust ingress
2 parents 9748c53 + 869a79e commit 517f855

File tree

9 files changed

+253
-3
lines changed

9 files changed

+253
-3
lines changed

.github/workflows/helm-ci.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ jobs:
6060
- name: Template monitoring chart
6161
run: helm template monitoring helm/monitoring/ > /dev/null
6262

63+
tunnel-lint:
64+
runs-on: ubuntu-latest
65+
steps:
66+
- uses: actions/checkout@v4
67+
- uses: azure/setup-helm@v4
68+
with:
69+
version: v3.14.0
70+
- name: Lint tunnel chart
71+
run: helm lint helm/cloudflare-tunnel/
72+
- name: Template tunnel chart
73+
run: helm template tunnel helm/cloudflare-tunnel/ --set tunnel.token=dummy-token > /dev/null
74+
6375
kubeconform:
6476
runs-on: ubuntu-latest
6577
needs: helm-template
@@ -99,7 +111,7 @@ jobs:
99111
run: pip install yamllint
100112
- name: Lint YAML files
101113
run: |
102-
yamllint -d relaxed helm/disentangle/Chart.yaml helm/disentangle/values.yaml helm/disentangle/values.schema.json helm/monitoring/Chart.yaml helm/monitoring/values.yaml gitops/
114+
yamllint -d relaxed helm/disentangle/Chart.yaml helm/disentangle/values.yaml helm/disentangle/values.schema.json helm/monitoring/Chart.yaml helm/monitoring/values.yaml helm/cloudflare-tunnel/Chart.yaml helm/cloudflare-tunnel/values.yaml gitops/
103115
104116
helm-unittest:
105117
runs-on: ubuntu-latest

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Thumbs.db
1111
# Helm
1212
helm/disentangle/charts/
1313
helm/monitoring/charts/
14+
helm/cloudflare-tunnel/charts/
1415
*.tgz
1516
.helm/
1617
helm-plugins/

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
.PHONY: help lint test test-unit test-golden test-policy test-integration test-all clean lint-monitoring template-monitoring deps-monitoring
1+
.PHONY: help lint test test-unit test-golden test-policy test-integration test-all clean lint-monitoring template-monitoring deps-monitoring lint-tunnel template-tunnel
22

33
CHART_DIR := helm/disentangle
44
MONITORING_CHART_DIR := helm/monitoring
5+
TUNNEL_CHART_DIR := helm/cloudflare-tunnel
56
NAMESPACE := disentangle-test
67
RELEASE := test-release
78

@@ -10,7 +11,7 @@ help: ## Show this help
1011

1112
# === Linting ===
1213

13-
lint: lint-helm lint-monitoring lint-yaml lint-shell ## Run all linters
14+
lint: lint-helm lint-monitoring lint-tunnel lint-yaml lint-shell ## Run all linters
1415

1516
lint-helm: ## Lint Helm chart
1617
helm lint $(CHART_DIR)
@@ -24,6 +25,12 @@ template-monitoring: ## Render monitoring chart templates
2425
deps-monitoring: ## Build monitoring chart dependencies
2526
helm dependency build $(MONITORING_CHART_DIR)
2627

28+
lint-tunnel: ## Lint cloudflare-tunnel chart
29+
helm lint $(TUNNEL_CHART_DIR)
30+
31+
template-tunnel: ## Render cloudflare-tunnel templates
32+
helm template tunnel $(TUNNEL_CHART_DIR) --set tunnel.token=dummy-token > /dev/null
33+
2734
lint-yaml: ## Lint YAML files (excluding Helm templates)
2835
@command -v yamllint >/dev/null 2>&1 || { echo "Install yamllint: pip install yamllint"; exit 1; }
2936
yamllint -d relaxed gitops/ $(CHART_DIR)/Chart.yaml $(CHART_DIR)/values.yaml

helm/cloudflare-tunnel/.helmignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Patterns to ignore when building packages.
2+
# This supports shell glob matching, relative path matching, and
3+
# negation (prefixed with !). Only one pattern per line.
4+
.DS_Store
5+
# Common VCS dirs
6+
.git/
7+
.gitignore
8+
.bzr/
9+
.bzrignore
10+
.hg/
11+
.hgignore
12+
.svn/
13+
# Common backup files
14+
*.swp
15+
*.bak
16+
*.tmp
17+
*.orig
18+
*~
19+
# Various IDEs
20+
.project
21+
.idea/
22+
*.tmproj
23+
.vscode/

helm/cloudflare-tunnel/Chart.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: v2
2+
name: cloudflare-tunnel
3+
description: Cloudflare Tunnel (cloudflared) for zero-trust ingress to Disentangle Network services
4+
type: application
5+
version: 0.1.0
6+
appVersion: "2026.2.0"
7+
license: Apache-2.0
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "cloudflare-tunnel.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully qualified app name.
10+
*/}}
11+
{{- define "cloudflare-tunnel.fullname" -}}
12+
{{- if .Values.fullnameOverride }}
13+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
14+
{{- else }}
15+
{{- $name := default .Chart.Name .Values.nameOverride }}
16+
{{- if contains $name .Release.Name }}
17+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
18+
{{- else }}
19+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
20+
{{- end }}
21+
{{- end }}
22+
{{- end }}
23+
24+
{{/*
25+
Create chart name and version as used by the chart label.
26+
*/}}
27+
{{- define "cloudflare-tunnel.chart" -}}
28+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
29+
{{- end }}
30+
31+
{{/*
32+
Common labels
33+
*/}}
34+
{{- define "cloudflare-tunnel.labels" -}}
35+
helm.sh/chart: {{ include "cloudflare-tunnel.chart" . }}
36+
{{ include "cloudflare-tunnel.selectorLabels" . }}
37+
{{- if .Chart.AppVersion }}
38+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
39+
{{- end }}
40+
app.kubernetes.io/managed-by: {{ .Release.Service }}
41+
{{- end }}
42+
43+
{{/*
44+
Selector labels
45+
*/}}
46+
{{- define "cloudflare-tunnel.selectorLabels" -}}
47+
app.kubernetes.io/name: {{ include "cloudflare-tunnel.name" . }}
48+
app.kubernetes.io/instance: {{ .Release.Name }}
49+
{{- end }}
50+
51+
{{/*
52+
Create the image string
53+
*/}}
54+
{{- define "cloudflare-tunnel.image" -}}
55+
{{- $tag := .Values.image.tag | default .Chart.AppVersion }}
56+
{{- printf "%s:%s" .Values.image.repository $tag }}
57+
{{- end }}
58+
59+
{{/*
60+
Determine the secret name for the tunnel token
61+
*/}}
62+
{{- define "cloudflare-tunnel.secretName" -}}
63+
{{- if .Values.tunnel.existingSecret }}
64+
{{- .Values.tunnel.existingSecret }}
65+
{{- else }}
66+
{{- include "cloudflare-tunnel.fullname" . }}-token
67+
{{- end }}
68+
{{- end }}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{{- if and (not .Values.tunnel.token) (not .Values.tunnel.existingSecret) }}
2+
{{- fail "Either tunnel.token or tunnel.existingSecret must be set" }}
3+
{{- end }}
4+
apiVersion: apps/v1
5+
kind: Deployment
6+
metadata:
7+
name: {{ include "cloudflare-tunnel.fullname" . }}
8+
labels:
9+
{{- include "cloudflare-tunnel.labels" . | nindent 4 }}
10+
spec:
11+
replicas: {{ .Values.replicaCount }}
12+
selector:
13+
matchLabels:
14+
{{- include "cloudflare-tunnel.selectorLabels" . | nindent 6 }}
15+
template:
16+
metadata:
17+
labels:
18+
{{- include "cloudflare-tunnel.selectorLabels" . | nindent 8 }}
19+
spec:
20+
{{- with .Values.podSecurityContext }}
21+
securityContext:
22+
{{- toYaml . | nindent 8 }}
23+
{{- end }}
24+
containers:
25+
- name: cloudflared
26+
image: {{ include "cloudflare-tunnel.image" . }}
27+
imagePullPolicy: {{ .Values.image.pullPolicy }}
28+
command:
29+
- cloudflared
30+
- tunnel
31+
- --no-autoupdate
32+
- run
33+
- --token
34+
- $(TUNNEL_TOKEN)
35+
env:
36+
- name: TUNNEL_TOKEN
37+
valueFrom:
38+
secretKeyRef:
39+
name: {{ include "cloudflare-tunnel.secretName" . }}
40+
key: token
41+
readinessProbe:
42+
httpGet:
43+
path: /ready
44+
port: 2000
45+
initialDelaySeconds: 5
46+
periodSeconds: 10
47+
{{- with .Values.securityContext }}
48+
securityContext:
49+
{{- toYaml . | nindent 12 }}
50+
{{- end }}
51+
{{- with .Values.resources }}
52+
resources:
53+
{{- toYaml . | nindent 12 }}
54+
{{- end }}
55+
{{- with .Values.nodeSelector }}
56+
nodeSelector:
57+
{{- toYaml . | nindent 8 }}
58+
{{- end }}
59+
{{- with .Values.affinity }}
60+
affinity:
61+
{{- toYaml . | nindent 8 }}
62+
{{- end }}
63+
{{- with .Values.tolerations }}
64+
tolerations:
65+
{{- toYaml . | nindent 8 }}
66+
{{- end }}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{{- if and .Values.tunnel.token (not .Values.tunnel.existingSecret) }}
2+
apiVersion: v1
3+
kind: Secret
4+
metadata:
5+
name: {{ include "cloudflare-tunnel.fullname" . }}-token
6+
labels:
7+
{{- include "cloudflare-tunnel.labels" . | nindent 4 }}
8+
type: Opaque
9+
data:
10+
token: {{ .Values.tunnel.token | b64enc | quote }}
11+
{{- end }}

helm/cloudflare-tunnel/values.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Cloudflare Tunnel configuration
2+
# Requires a tunnel token from the Cloudflare Zero Trust dashboard
3+
# Create at: https://one.dash.cloudflare.com/ -> Networks -> Tunnels
4+
5+
# Tunnel authentication
6+
tunnel:
7+
# Existing secret name containing the tunnel token
8+
# The secret must have key 'token' with the tunnel token value
9+
existingSecret: ""
10+
# Or specify the token directly (not recommended for production)
11+
token: ""
12+
13+
image:
14+
repository: cloudflare/cloudflared
15+
tag: "" # defaults to appVersion
16+
pullPolicy: IfNotPresent
17+
18+
replicaCount: 1
19+
20+
resources:
21+
requests:
22+
cpu: 100m
23+
memory: 128Mi
24+
limits:
25+
cpu: 500m
26+
memory: 256Mi
27+
28+
# Pod security
29+
podSecurityContext:
30+
runAsNonRoot: true
31+
seccompProfile:
32+
type: RuntimeDefault
33+
34+
securityContext:
35+
allowPrivilegeEscalation: false
36+
capabilities:
37+
drop: [ALL]
38+
readOnlyRootFilesystem: true
39+
runAsNonRoot: true
40+
runAsUser: 65532 # nonroot user
41+
42+
# Ingress rules for the tunnel
43+
# Maps external hostnames to internal K8s services
44+
ingressRules: []
45+
# - hostname: rpc.disentangle.network
46+
# service: http://disentangle-rpc.disentangle.svc.cluster.local:8000
47+
# - hostname: grafana.disentangle.network
48+
# service: http://disentangle-monitoring-grafana.monitoring.svc.cluster.local:80
49+
50+
# Catch-all rule for unmatched requests
51+
catchAllService: http_status:404
52+
53+
nodeSelector: {}
54+
tolerations: []
55+
affinity: {}

0 commit comments

Comments
 (0)