Migrate cloudflare-tunnel to helmchart

This commit is contained in:
dvirlabs 2025-06-12 20:40:29 +03:00
parent feb89d4a74
commit 6e357b574e
11 changed files with 510 additions and 0 deletions

View File

@ -0,0 +1,21 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cloudflared
namespace: argocd
spec:
project: infra
destination:
server: https://kubernetes.default.svc
namespace: traefik
source:
repoURL: https://git.dvirlabs.com/dvirlabs/infra.git
targetRevision: HEAD
path: charts/cloudflared
helm:
valueFiles:
- ../../manifests/cloudflared/values.yaml
syncPolicy:
automated:
selfHeal: true
prune: false # set to true once you're ready to delete the old deployment

View File

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -0,0 +1,22 @@
apiVersion: v2
name: cloudflare-tunnel
description: Creation of a cloudflared deployment - a reverse tunnel for an environment
# A chart can be either an 'application' or a 'library' chart.
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.3.2
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "2024.8.3"
# Let's look professional with a Cloudflare icon.
icon: https://developers.cloudflare.com/cloudflare-one/favicon-32x32.png

View File

@ -0,0 +1,51 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "cloudflare-tunnel.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "cloudflare-tunnel.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "cloudflare-tunnel.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "cloudflare-tunnel.labels" -}}
helm.sh/chart: {{ include "cloudflare-tunnel.chart" . }}
{{ include "cloudflare-tunnel.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "cloudflare-tunnel.selectorLabels" -}}
app.kubernetes.io/name: {{ include "cloudflare-tunnel.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@ -0,0 +1,29 @@
# This configmap stores the configuration used by cloudflared.
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "cloudflare-tunnel.fullname" . }}
data:
config.yaml: |
# Name of the tunnel you want to run
tunnel: {{ .Values.cloudflare.tunnelName }}
# The location of the secret containing the tunnel credentials
credentials-file: /etc/cloudflared/creds/credentials.json
# General purpose TCP routing for the network
warp-routing:
enabled: {{ .Values.cloudflare.enableWarp }}
# Serves the metrics server under /metrics and the readiness server under /ready
metrics: 0.0.0.0:2000
# Autoupdates applied in a k8s pod will be lost when the pod is removed or restarted, so
# autoupdate doesn't make sense in Kubernetes. However, outside of Kubernetes, we strongly
# recommend using autoupdate.
no-autoupdate: true
# The `ingress` block tells cloudflared which local service to route incoming
# requests to. For more about ingress rules, see
# https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ingress
ingress:
{{- with .Values.cloudflare.ingress }}
{{- toYaml . | nindent 6 }}
{{- end }}
# This rule matches any traffic which didn't match a previous rule, and responds with HTTP 404.
- service: http_status:404

View File

@ -0,0 +1,105 @@
# Here we deploy cloudflared images. The tunnel credentials are stored in
# a k8s secret, and the configuration is stored in a k8s configmap.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "cloudflare-tunnel.fullname" . }}
labels:
{{- include "cloudflare-tunnel.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "cloudflare-tunnel.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
# These are here so the deployment rolls when the config or secret change.
checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "cloudflare-tunnel.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "cloudflare-tunnel.fullname" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- tunnel
# Points cloudflared to the config file, which configures what
# cloudflared will actually do. This file is created by a ConfigMap.
- --config
- /etc/cloudflared/config/config.yaml
- run
livenessProbe:
httpGet:
# Cloudflared has a /ready endpoint which returns 200 if and only if
# it has an active connection to the edge.
path: /ready
port: 2000
failureThreshold: 1
initialDelaySeconds: 10
periodSeconds: 10
volumeMounts:
- name: config
mountPath: /etc/cloudflared/config
readOnly: true
# Each tunnel has an associated "credentials file" which authorizes machines
# to run the tunnel. cloudflared will read this file from its local filesystem,
# and it'll be stored in a k8s secret.
- name: creds
mountPath: /etc/cloudflared/creds
readOnly: true
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
- name: creds
secret:
secretName: {{ .Values.cloudflare.secretName | default (include "cloudflare-tunnel.fullname" .) }}
- name: config
configMap:
name: {{ include "cloudflare-tunnel.fullname" . }}
items:
- key: config.yaml
path: config.yaml
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
affinity:
{{- with .Values.affinity }}
{{- toYaml . | nindent 8 }}
{{- else }}
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 10
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchExpressions:
{{- range $k, $v := include "cloudflare-tunnel.selectorLabels" . | fromYaml }}
- key: {{ $k }}
operator: In
values:
- {{ $v }}
{{- end }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -0,0 +1,17 @@
{{- if and (and .Values.cloudflare.account .Values.cloudflare.tunnelId .Values.cloudflare.secret) (not .Values.cloudflare.secretName) }}
# This credentials secret allows cloudflared to authenticate itself
# to the Cloudflare infrastructure.
apiVersion: v1
kind: Secret
metadata:
name: {{ include "cloudflare-tunnel.fullname" . }}
labels:
{{- include "cloudflare-tunnel.labels" . | nindent 4 }}
stringData:
credentials.json: |-
{
"AccountTag": {{ .Values.cloudflare.account | quote }},
"TunnelID": {{ .Values.cloudflare.tunnelId | quote }},
"TunnelSecret": {{ .Values.cloudflare.secret | quote }}
}
{{- end }}

View File

@ -0,0 +1,12 @@
# Here we create a service account with no privileges to run the
# deployment - just in case the default service account is different.
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "cloudflare-tunnel.fullname" . }}
labels:
{{- include "cloudflare-tunnel.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}

View File

@ -0,0 +1,82 @@
# Default values for cloudflare-tunnel.
# Cloudflare parameters.
cloudflare:
# Your Cloudflare account number.
account: ""
# The name of the tunnel this instance will serve
tunnelName: ""
# The ID of the above tunnel.
tunnelId: ""
# The secret for the tunnel.
secret: ""
# If defined, no secret is created for the credentials, and instead, the secret referenced is used
secretName: null
# If true, turn on WARP routing for TCP
enableWarp: false
# Define ingress rules for the tunnel. See
# https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file/ingress
ingress: []
# The first rule proxies traffic to the httpbin sample service named web-server at port 80
# - hostname: tunnel.example.com
# service: http://web-service:80
# This rule sends traffic to the built-in hello-world HTTP server. This can help debug connectivity
# issues. If hello.example.com resolves and tunnel.example.com does not, then the problem is
# in the connection from cloudflared to your local service, not from the internet to cloudflared.
# - hostname: hello.example.com
# service: hello_world
image:
repository: cloudflare/cloudflared
pullPolicy: IfNotPresent
# If supplied, this overrides "appVersion"
tag: ""
replicaCount: 2
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podLabels: {}
# Security items common to everything in the pod. Here we require that it
# does not run as the user defined in the image, literally named "nonroot".
podSecurityContext:
runAsNonRoot: true
runAsUser: 65532
# Security items for one container. We lock it down.
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
# Default affinity is to spread out over nodes; use this to override.
affinity: {}

135
cloudflared-config.yaml Normal file
View File

@ -0,0 +1,135 @@
apiVersion: v1
data:
config.yaml: |
tunnel: b50bbf48-0a2f-47ce-b73e-336b6718318b
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: traefik.dvirlabs.com
service: http://traefik-dashboard.traefik.svc.cluster.local:8080
- hostname: rancher.dvirlabs.com
service: https://rancher.cattle-system.svc.cluster.local:443
originRequest:
noTLSVerify: true
- hostname: grafana.dvirlabs.com
service: http://grafana-prod.monitoring.svc.cluster.local:80
- hostname: grafana-int.dvirlabs.com
service: http://grafana-int.monitoring.svc.cluster.local:80
- hostname: pve.dvirlabs.com
service: https://192.168.10.120:8006
originRequest:
noTLSVerify: true
- hostname: git.dvirlabs.com
service: http://gitea-http.dev-tools.svc.cluster.local:3000
- hostname: woodpecker.dvirlabs.com
service: http://woodpecker-server.dev-tools.svc.cluster.local:80
- hostname: argocd.dvirlabs.com
service: http://argocd-server.argocd.svc.cluster.local:80
- hostname: prometheus.dvirlabs.com
service: http://prometheus-prod-server.monitoring.svc.cluster.local:80
- hostname: prometheus-int.dvirlabs.com
service: http://prometheus-int-server.monitoring.svc.cluster.local:80
- hostname: wiki.dvirlabs.com
service: http://wikijs.dev-tools.svc.cluster.local:3000
- hostname: minio.dvirlabs.com
service: http://minio-bitnami.infra.svc.cluster.local:9001
- hostname: s3.dvirlabs.com
service: http://minio-bitnami.infra.svc.cluster.local:9000
- hostname: keycloak.dvirlabs.com
service: http://keycloak.infra.svc.cluster.local:8080
- hostname: vault.dvirlabs.com
service: http://vault-ui.dev-tools.svc.cluster.local:8200
- hostname: kibana.dvirlabs.com
service: http://kibana-prod-kb-http.monitoring.svc.cluster.local:5601
- hostname: kibana-int.dvirlabs.com
service: http://kibana-int-kb-http.monitoring.svc.cluster.local:5601
- hostname: oramap.dvirlabs.com
service: http://oramap-oramap.my-apps.svc.cluster.local:80
- hostname: navix.dvirlabs.com
service: http://navix-frontend.my-apps.svc.cluster.local:80
- hostname: webmail.dvirlabs.com
service: https://mailu-front.mail-services.svc.cluster.local:443
originRequest:
noTLSVerify: true
- hostname: harbor.dvirlabs.com
service: https://traefik.traefik.svc.cluster.local:443
originRequest:
originServerName: harbor.dvirlabs.com
noTLSVerify: true
- hostname: nas.dvirlabs.com
service: https://192.168.10.155:10000
originRequest:
noTLSVerify: true
- hostname: vered.dvirlabs.com
service: http://192.168.10.118:1212
- service: http_status:404
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: >
{"apiVersion":"v1","data":{"config.yaml":"tunnel:
b50bbf48-0a2f-47ce-b73e-336b6718318b\ncredentials-file:
/etc/cloudflared/credentials.json\n\ningress:\n - hostname:
traefik.dvirlabs.com\n service:
http://traefik-dashboard.traefik.svc.cluster.local:8080\n\n - hostname:
rancher.dvirlabs.com\n service:
https://rancher.cattle-system.svc.cluster.local:443\n
originRequest:\n noTLSVerify: true\n\n - hostname:
woodpecker.dvirlabs.com\n service:
http://woodpecker.woodpecker.svc.cluster.local:80\n\n - hostname:
gitea.dvirlabs.com\n service:
http://gitea.gitea.svc.cluster.local:3000\n\n - hostname:
grafana.dvirlabs.com\n #service:
http://traefik.traefik.svc.cluster.local:8000\n service:
http://192.168.10.170:31612\n\n\n\n - service:
http_status:404\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"cloudflared-config","namespace":"traefik"}}
creationTimestamp: '2025-04-13T19:36:21Z'
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl-client-side-apply
operation: Update
time: '2025-04-15T23:19:06Z'
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
f:config.yaml: {}
manager: rancher
operation: Update
time: '2025-06-04T22:52:30Z'
name: cloudflared-config
namespace: traefik
resourceVersion: '12779879'
uid: 3ed87e59-09b9-4993-9646-9f463a9c97f3

View File

@ -0,0 +1,13 @@
tunnel:
name: b50bbf48-0a2f-47ce-b73e-336b6718318b
credentialsSecretName: cloudflared-creds
config:
existingConfig: cloudflared-config
cloudflared:
image:
repository: cloudflare/cloudflared
tag: latest
metrics:
enabled: false