diff --git a/applications/gluetun.yaml b/applications/gluetun.yaml deleted file mode 100644 index 14ea24b..0000000 --- a/applications/gluetun.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: gluetun - namespace: argocd - annotations: -spec: - project: default - source: - repoURL: http://gitea-http.gitea.svc.cluster.local:3000/admin/turingpi.git - targetRevision: HEAD - path: custom_helm_charts/gluetun - helm: - releaseName: gluetun - valueFiles: - - ../../helm-values/gluetun_values.yaml - destination: - server: https://kubernetes.default.svc - namespace: default - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true - - ServerSideApply=true diff --git a/custom_helm_charts/gluetun/.helmignore b/custom_helm_charts/gluetun/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/custom_helm_charts/gluetun/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# 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 -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/custom_helm_charts/gluetun/Chart.yaml b/custom_helm_charts/gluetun/Chart.yaml deleted file mode 100644 index 02ee9a5..0000000 --- a/custom_helm_charts/gluetun/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: Gluetun - VPN client with HTTP proxy -name: gluetun -version: 0.1.0 -icon: https://raw.githubusercontent.com/qdm12/gluetun/master/.github/logo.png diff --git a/custom_helm_charts/gluetun/README.md b/custom_helm_charts/gluetun/README.md deleted file mode 100644 index e274eb0..0000000 --- a/custom_helm_charts/gluetun/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Usenet stack (Gluetun + NZBGet) - -Service endpoints: -- NZBGet UI: nzbget.default.svc.cluster.local:6789 -- Gluetun HTTP proxy: gluetun.default.svc.cluster.local:8888 - -AirVPN WireGuard values: -- Update `helm-values/gluetun_values.yaml`: - - `env.WIREGUARD_ADDRESSES` -> WireGuard tunnel address(es) (IPv4 /32 and optional IPv6) - - `env.SERVER_COUNTRIES` -> recommended AirVPN server selection (e.g. Netherlands) - - If your cluster does not support IPv6, remove the IPv6 address from `env.WIREGUARD_ADDRESSES`. -- Create a Secret named `gluetun-wireguard` with key `WIREGUARD_PRIVATE_KEY` from your AirVPN WireGuard config (do not commit the key). - - Add `WIREGUARD_PRESHARED_KEY` from the same AirVPN WireGuard config. - - `helm-values/gluetun_values.yaml` sets `secret.create: false` so the chart does not create a placeholder secret. - -Validation: -- ArgoCD health: `argocd app get gluetun` and `argocd app get nzbget` -- WireGuard up: `kubectl -n default logs deploy/gluetun | rg -i "wireguard|tunnel"` -- VPN egress from NZBGet: `kubectl -n default exec deploy/nzbget -- curl -s ifconfig.me` -- NZBGet UI: `kubectl -n default port-forward deploy/nzbget 6789:6789` - -Note: NZBGet is configured to use the HTTP proxy via `HTTP_PROXY`/`HTTPS_PROXY`. If your NNTP traffic does not honor proxy settings, consider using a proxy-aware downloader or running the downloader in the same pod as Gluetun. diff --git a/custom_helm_charts/gluetun/templates/NOTES.txt b/custom_helm_charts/gluetun/templates/NOTES.txt deleted file mode 100644 index cd019b5..0000000 --- a/custom_helm_charts/gluetun/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "gluetun.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "gluetun.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "gluetun.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "gluetun.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8888 to use your application" - kubectl port-forward $POD_NAME 8888:{{ .Values.service.port }} -{{- end }} diff --git a/custom_helm_charts/gluetun/templates/_helpers.tpl b/custom_helm_charts/gluetun/templates/_helpers.tpl deleted file mode 100644 index c58d32c..0000000 --- a/custom_helm_charts/gluetun/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "gluetun.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 "gluetun.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 "gluetun.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/custom_helm_charts/gluetun/templates/deployment.yaml b/custom_helm_charts/gluetun/templates/deployment.yaml deleted file mode 100644 index d719370..0000000 --- a/custom_helm_charts/gluetun/templates/deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "gluetun.fullname" . }} - labels: - app: {{ template "gluetun.name" . }} - chart: {{ template "gluetun.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "gluetun.name" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "gluetun.name" . }} - release: {{ .Release.Name }} - spec: - volumes: -{{ toYaml .Values.volumes | indent 6 }} - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - securityContext: -{{ toYaml .Values.securityContext | indent 12 }} - env: -{{ toYaml .Values.env | indent 12 }} - ports: - - name: http-proxy - containerPort: {{ .Values.service.port }} - protocol: TCP - livenessProbe: -{{ toYaml .Values.livenessProbe | indent 12 }} - readinessProbe: -{{ toYaml .Values.readinessProbe | indent 12 }} - volumeMounts: -{{ toYaml .Values.volumeMounts | indent 12 }} - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} diff --git a/custom_helm_charts/gluetun/templates/ingress.yaml b/custom_helm_charts/gluetun/templates/ingress.yaml deleted file mode 100644 index 3d57dee..0000000 --- a/custom_helm_charts/gluetun/templates/ingress.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "gluetun.fullname" . -}} -{{- $ingressPath := .Values.ingress.path -}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app: {{ template "gluetun.name" . }} - chart: {{ template "gluetun.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- with .Values.ingress.annotations }} - annotations: -{{ toYaml . | indent 4 }} -{{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ . }} - http: - paths: - - path: {{ $ingressPath }} - backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} -{{- end }} diff --git a/custom_helm_charts/gluetun/templates/secret.yaml b/custom_helm_charts/gluetun/templates/secret.yaml deleted file mode 100644 index a665716..0000000 --- a/custom_helm_charts/gluetun/templates/secret.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.secret.create -}} -apiVersion: v1 -kind: Secret -metadata: - name: {{ .Values.secret.name }} - labels: - app: {{ template "gluetun.name" . }} - chart: {{ template "gluetun.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -type: Opaque -stringData: - WIREGUARD_PRIVATE_KEY: {{ .Values.secret.privateKey | quote }} - WIREGUARD_PRESHARED_KEY: {{ .Values.secret.presharedKey | quote }} -{{- end }} diff --git a/custom_helm_charts/gluetun/templates/service.yaml b/custom_helm_charts/gluetun/templates/service.yaml deleted file mode 100644 index e8805c4..0000000 --- a/custom_helm_charts/gluetun/templates/service.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "gluetun.fullname" . }} - labels: - app: {{ template "gluetun.name" . }} - chart: {{ template "gluetun.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: {{ .Values.service.port }} - protocol: TCP - name: http-proxy - selector: - app: {{ template "gluetun.name" . }} - release: {{ .Release.Name }} diff --git a/custom_helm_charts/gluetun/values.yaml b/custom_helm_charts/gluetun/values.yaml deleted file mode 100644 index 732f754..0000000 --- a/custom_helm_charts/gluetun/values.yaml +++ /dev/null @@ -1,102 +0,0 @@ -replicaCount: 1 - -image: - repository: qmcgaw/gluetun - tag: latest - pullPolicy: IfNotPresent - -env: - - name: VPN_SERVICE_PROVIDER - value: "airvpn" - - name: VPN_TYPE - value: "wireguard" - - name: WIREGUARD_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: gluetun-wireguard - key: WIREGUARD_PRIVATE_KEY - - name: WIREGUARD_PRESHARED_KEY - valueFrom: - secretKeyRef: - name: gluetun-wireguard - key: WIREGUARD_PRESHARED_KEY - - name: WIREGUARD_ADDRESSES - value: "REPLACE_ME" - - name: SERVER_COUNTRIES - value: "REPLACE_ME" - - name: HTTPPROXY - value: "on" - - name: HTTPPROXY_LOG - value: "off" - - name: FIREWALL_INPUT_PORTS - value: "8888" - - name: TZ - value: "Europe/Amsterdam" - -secret: - create: true - name: gluetun-wireguard - privateKey: "REPLACE_ME" - presharedKey: "REPLACE_ME" - -service: - type: ClusterIP - port: 8888 - -ingress: - enabled: false - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - path: / - hosts: - - gluetun.example.org - tls: - - secretName: gluetun-example-org - hosts: - - gluetun.example.org - -volumes: - - name: dev-tun - hostPath: - path: /dev/net/tun - -volumeMounts: - - name: dev-tun - mountPath: "/dev/net/tun" - -securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - -livenessProbe: - tcpSocket: - port: 8888 - initialDelaySeconds: 10 - periodSeconds: 20 - timeoutSeconds: 2 - failureThreshold: 3 - -readinessProbe: - tcpSocket: - port: 8888 - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 2 - failureThreshold: 3 - -resources: - requests: - memory: 128Mi - cpu: 100m - limits: - memory: 512Mi - cpu: 500m - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/custom_helm_charts/nzbget/templates/deployment.yaml b/custom_helm_charts/nzbget/templates/deployment.yaml index a24acf3..e5344f1 100644 --- a/custom_helm_charts/nzbget/templates/deployment.yaml +++ b/custom_helm_charts/nzbget/templates/deployment.yaml @@ -26,6 +26,21 @@ spec: {{ toYaml . | indent 8 }} {{- end }} containers: + - name: gluetun + image: "{{ .Values.gluetun.image.repository }}:{{ .Values.gluetun.image.tag }}" + imagePullPolicy: {{ .Values.gluetun.image.pullPolicy }} + securityContext: +{{ toYaml .Values.gluetun.securityContext | indent 12 }} + env: +{{ toYaml .Values.gluetun.env | indent 12 }} + livenessProbe: +{{ toYaml .Values.gluetun.livenessProbe | indent 12 }} + readinessProbe: +{{ toYaml .Values.gluetun.readinessProbe | indent 12 }} + volumeMounts: +{{ toYaml .Values.gluetun.volumeMounts | indent 12 }} + resources: +{{ toYaml .Values.gluetun.resources | indent 12 }} - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} diff --git a/helm-values/gluetun_values.yaml b/helm-values/gluetun_values.yaml deleted file mode 100644 index 1edb545..0000000 --- a/helm-values/gluetun_values.yaml +++ /dev/null @@ -1,88 +0,0 @@ -replicaCount: 1 - -image: - repository: qmcgaw/gluetun - tag: "latest" - pullPolicy: Always - -env: - - name: VPN_SERVICE_PROVIDER - value: "airvpn" - - name: VPN_TYPE - value: "wireguard" - - name: WIREGUARD_PRIVATE_KEY - valueFrom: - secretKeyRef: - name: gluetun-wireguard - key: WIREGUARD_PRIVATE_KEY - - name: WIREGUARD_PRESHARED_KEY - valueFrom: - secretKeyRef: - name: gluetun-wireguard - key: WIREGUARD_PRESHARED_KEY - - name: WIREGUARD_ADDRESSES - value: "10.160.17.207/32,fd7d:76ee:e68f:a993:61d7:a5fe:f834:90e1/128" - - name: SERVER_COUNTRIES - value: "Netherlands" - - name: HTTPPROXY - value: "on" - - name: HTTPPROXY_LOG - value: "off" - - name: FIREWALL_INPUT_PORTS - value: "8888" - - name: TZ - value: "Europe/Amsterdam" - -secret: - create: false - name: gluetun-wireguard - privateKey: "REPLACE_ME" - -service: - type: ClusterIP - port: 8888 - -volumes: - - name: dev-tun - hostPath: - path: /dev/net/tun - -volumeMounts: - - name: dev-tun - mountPath: "/dev/net/tun" - -securityContext: - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - -livenessProbe: - tcpSocket: - port: 8888 - initialDelaySeconds: 10 - periodSeconds: 20 - timeoutSeconds: 2 - failureThreshold: 3 - -readinessProbe: - tcpSocket: - port: 8888 - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 2 - failureThreshold: 3 - -resources: - requests: - memory: 128Mi - cpu: 100m - limits: - memory: 512Mi - cpu: 500m - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/helm-values/nzbget_values.yaml b/helm-values/nzbget_values.yaml index 17a1fff..18e0563 100644 --- a/helm-values/nzbget_values.yaml +++ b/helm-values/nzbget_values.yaml @@ -12,16 +12,76 @@ env: value: "1000" - name: TZ value: "Europe/Amsterdam" - - name: HTTP_PROXY - value: "http://gluetun.default.svc.cluster.local:8888" - - name: http_proxy - value: "http://gluetun.default.svc.cluster.local:8888" - - name: HTTPS_PROXY - value: "http://gluetun.default.svc.cluster.local:8888" - - name: https_proxy - value: "http://gluetun.default.svc.cluster.local:8888" - - name: NO_PROXY - value: "localhost,127.0.0.1,.svc,.cluster.local" + +# gluetun runs as a sidecar in this pod (same pattern as qbittorrent): it shares +# the pod network namespace and installs the WireGuard tunnel + a kill-switch, so +# ALL of nzbget's traffic — including NNTP (port 563) to newshosting — egresses +# through the VPN. (An HTTP proxy can't cover NNTP, which is why the old +# HTTP_PROXY-to-standalone-gluetun approach left usenet downloads going direct.) +# Uses its own AirVPN device/secret (gluetun-wireguard-nzbget) to avoid sharing a +# WireGuard IP with the qbittorrent tunnel. Keep DOT=off + DNS_ADDRESS — see the +# AirVPN-blocks-DoT gotcha in CLAUDE.md. +gluetun: + image: + repository: qmcgaw/gluetun + tag: v3.41.1 + pullPolicy: IfNotPresent + env: + - name: VPN_SERVICE_PROVIDER + value: "airvpn" + - name: VPN_TYPE + value: "wireguard" + - name: WIREGUARD_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: gluetun-wireguard-nzbget + key: WIREGUARD_PRIVATE_KEY + - name: WIREGUARD_PRESHARED_KEY + valueFrom: + secretKeyRef: + name: gluetun-wireguard-nzbget + key: WIREGUARD_PRESHARED_KEY + - name: WIREGUARD_ADDRESSES + value: "10.166.207.220/32,fd7d:76ee:e68f:a993:bd5e:ddfc:ad2c:d30c/128" + - name: SERVER_COUNTRIES + value: "Netherlands" + - name: DOT + value: "off" + - name: DNS_ADDRESS + value: "10.128.0.1" + - name: FIREWALL_INPUT_PORTS + value: "6789" + - name: TZ + value: "Europe/Amsterdam" + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + livenessProbe: + tcpSocket: + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 20 + timeoutSeconds: 2 + failureThreshold: 3 + readinessProbe: + tcpSocket: + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 2 + failureThreshold: 3 + resources: + requests: + memory: 128Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 500m + volumeMounts: + - name: dev-tun + mountPath: "/dev/net/tun" # nzbget cannot read server credentials from environment variables (its # ${...} config syntax only references other nzbget options, not env). So an @@ -76,6 +136,9 @@ volumes: - name: plex-data persistentVolumeClaim: claimName: "plex-data" + - name: dev-tun + hostPath: + path: /dev/net/tun volumeMounts: - name: plex-data