diff --git a/Chart.yaml b/Chart.yaml index e2df016..121ebbe 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: 'v2' name: 'media-servarr-base' description: 'Base chart for media-servarr charts' type: 'application' -version: 0.5.1 +version: 0.6.0 icon: 'https://github.com/drinkataco/media-servarr/blob/main/icon.png' diff --git a/charts/bazarr/Chart.yaml b/charts/bazarr/Chart.yaml index 4149268..92656ad 100644 --- a/charts/bazarr/Chart.yaml +++ b/charts/bazarr/Chart.yaml @@ -13,12 +13,12 @@ keywords: - 'bazarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.4.5 +version: 0.5.0 appVersion: '1.4.5' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/bazarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/flaresolverr/Chart.yaml b/charts/flaresolverr/Chart.yaml index a4fdf7e..e125e0e 100644 --- a/charts/flaresolverr/Chart.yaml +++ b/charts/flaresolverr/Chart.yaml @@ -9,12 +9,12 @@ keywords: - 'bypass' kubeVersion: ">=1.24.0-0" type: 'application' -version: 0.4.4 +version: 0.5.0 appVersion: 'v3.3.21' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/flaresolverr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/homarr/Chart.yaml b/charts/homarr/Chart.yaml index a6bbfe9..4a2ff20 100644 --- a/charts/homarr/Chart.yaml +++ b/charts/homarr/Chart.yaml @@ -7,12 +7,12 @@ keywords: - 'homarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.5.4 +version: 0.6.0 appVersion: '0.15.4' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/radarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/jellyfin/Chart.yaml b/charts/jellyfin/Chart.yaml index c237238..f02ef00 100644 --- a/charts/jellyfin/Chart.yaml +++ b/charts/jellyfin/Chart.yaml @@ -15,12 +15,12 @@ keywords: - 'jellyfin' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.3.4 +version: 0.4.0 appVersion: '10.9.11' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/jellyfin/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/jellyseerr/Chart.yaml b/charts/jellyseerr/Chart.yaml index cd21bae..b1c5780 100644 --- a/charts/jellyseerr/Chart.yaml +++ b/charts/jellyseerr/Chart.yaml @@ -13,12 +13,12 @@ keywords: - 'jellyseerr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.2.2 +version: 0.3.0 appVersion: '1.9.2' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/jellyseerr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/lidarr/Chart.yaml b/charts/lidarr/Chart.yaml index 35e23c9..436d27e 100644 --- a/charts/lidarr/Chart.yaml +++ b/charts/lidarr/Chart.yaml @@ -11,12 +11,12 @@ keywords: - 'lidarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.6.0 +version: 0.7.0 appVersion: '2.5.3' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/radarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/prowlarr/Chart.yaml b/charts/prowlarr/Chart.yaml index ff973e6..80712f9 100644 --- a/charts/prowlarr/Chart.yaml +++ b/charts/prowlarr/Chart.yaml @@ -10,12 +10,12 @@ keywords: - 'prowlarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.6.0 +version: 0.7.0 appVersion: '1.24.3' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/prowlarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/radarr/Chart.yaml b/charts/radarr/Chart.yaml index 1e1cf88..acfa3f4 100644 --- a/charts/radarr/Chart.yaml +++ b/charts/radarr/Chart.yaml @@ -12,12 +12,12 @@ keywords: - 'radarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.6.0 +version: 0.7.0 appVersion: '5.11.0' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/radarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/readarr/Chart.yaml b/charts/readarr/Chart.yaml index e5ca463..8a0fe3b 100644 --- a/charts/readarr/Chart.yaml +++ b/charts/readarr/Chart.yaml @@ -12,12 +12,12 @@ keywords: - 'readarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.6.0 +version: 0.7.0 appVersion: '0.4.0-develop' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/readarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/sabnzbd/Chart.yaml b/charts/sabnzbd/Chart.yaml index 7bd58be..76cd4a1 100644 --- a/charts/sabnzbd/Chart.yaml +++ b/charts/sabnzbd/Chart.yaml @@ -8,12 +8,12 @@ keywords: - 'sabnzbd' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.1.3 +version: 0.2.0 appVersion: '4.3.3' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/sabnzbd/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/sonarr/Chart.yaml b/charts/sonarr/Chart.yaml index 5abe148..2b62f27 100644 --- a/charts/sonarr/Chart.yaml +++ b/charts/sonarr/Chart.yaml @@ -13,12 +13,12 @@ keywords: - 'sonarr' kubeversion: ">=1.24.0-0" type: 'application' -version: 0.5.5 +version: 0.6.0 appVersion: '4.0.9' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/sonarr/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/transmission/Chart.yaml b/charts/transmission/Chart.yaml index 2d585d9..81699b3 100644 --- a/charts/transmission/Chart.yaml +++ b/charts/transmission/Chart.yaml @@ -10,12 +10,12 @@ keywords: - 'usenet' kubeVersion: ">=1.24.0-0" type: 'application' -version: 0.5.2 +version: 0.6.0 appVersion: '4.0.6' icon: 'https://github.com/drinkataco/media-servarr/blob/main/charts/transmission/icon.png' dependencies: - name: 'media-servarr-base' - version: 0.5.1 + version: 0.6.0 repository: "file://../.." maintainers: - name: 'media-servarr' diff --git a/charts/transmission/README.md b/charts/transmission/README.md index 12d502f..6695065 100644 --- a/charts/transmission/README.md +++ b/charts/transmission/README.md @@ -12,6 +12,7 @@ This README covers the basics of customising and installation * [Application Configuration](#application-configuration) * [Volumes](#volumes) * [Ingress Configuration](#ingress-configuration) + * [VPN Sidecar](#vpn-sidecar) * [Advanced](#advanced) * [Upgrading](#upgrading) * [Uninstallation](#uninstallation) @@ -105,6 +106,12 @@ ingress: enabled: true ``` +### VPN Sidecar + +Thanks to [qdm12/gluetun](https://github.com/qdm12/gluetun), it is fairly trivial to route traffic through a VPN. + +[To see how, view the VPN docs.](./docs/vpn.md) + ### Advanced Other supported deployment configuration include `deployment.nodeSelector`, `deployment.tolerations`, and `deployment.affinity` diff --git a/charts/transmission/docs/vpn.md b/charts/transmission/docs/vpn.md new file mode 100644 index 0000000..df728a2 --- /dev/null +++ b/charts/transmission/docs/vpn.md @@ -0,0 +1,200 @@ +# Routing Traffic through a VPN + +Here is a guide how you can route all of your torrent traffic through a VPN for transmission in kubernetes. + +This guide uses [gluetun](https://github.com/qdm12/gluetun) to simplify the process of forwarding traffic from your transmission container. + +It is heavily recommended to use a VPN such as [ProtonVPN](https://protonvpn.com/) that supports Port Forwarding! + +Configuring Gluetun will be slightly different for everybody, so please referr to their documentation along the way. + +## Configuration + +All configuration can be applied within your `values.yaml` file used for provisioning resources from this helm chart. + +### Transmission + +First, we must add a securityContext to transmission so that it can perform network-related administrative tasks alongside gluetun. + +```yaml +deployment: + container: + securityContext: + - 'NET_ADMIN' +``` + +### Gluetun + +We'll need to add a sidecar container to run Gluetun, with the same security context. + +Configuration specifics, in terms of environment variables, will be different for everybody. Consult the [Gluetun Wiki](https://github.com/qdm12/gluetun-wiki/)! + +```yaml +deployment: + ... + sideCarContainers: + - name: 'gluetun' + image: 'qmcgaw/gluetun' + ports: + - containerPort: 8000 + securityContext: + capabilities: + add: + - 'NET_ADMIN' + env: + # VPN connection + # This is different depending on your provider. Consult Gluetun documentation + - name: 'VPN_SERVICE_PROVIDER' + value: 'protonvpn' + - name: 'VPN_TYPE' + value: 'wireguard' + - name: 'WIREGUARD_PRIVATE_KEY' + value: '' + - name: 'SERVER_COUNTRIES' + value: 'United Kingdom' + # Port Forwarding + # A port + - name: 'VPN_PORT_FORWARDING' + value: 'on' + - name: 'PORT_FORWARD_ONLY' + value: 'on' + # Kubernetes specific config: + # DNS + - name: 'DNS_KEEP_NAMESERVER' + value: 'on' + # FIREWALL + - name: 'FIREWALL_OUTBOUND_SUBNETS' + value: '10.42.0.0/15' +``` + +## Deployment + +By adding these two items deploying your helm chart should now enable Transmission traffic to flow through your chosen VPN provider! + +Verify your setup with these commands: + +```bash +# Verify your allocated IP in Gluetun +kubectl exec -it \ + \ + --container gluetun \ + -- cat /tmp/gluetun/ip + +# Verify your assigned forwarded port in Gluetun +kubectl exec -it \ + \ + --container gluetun \ + -- cat /tmp/gluetun/forwarded_port + +# Verify your IP address in Transmission +kubectl exec -it \ + \ + --container transmission \ + -- curl ifconfig.me +``` + +Within the transmission UI, it is worth updating your peer port by going to _Menu_ > _Edit Preferences_ > _Network_, and the portchecker should verify this is an open port. + +> [!NOTE] +> Assigned forwarded ports are random each time, so you must update the peer port from the UI. Alternatively, you could add [another sidecar to automate this process](#auto-update-peer-port) for you + +## Bonus + +### Auto update peer port + +It can get a bit annoying managing the peer port manually through the transmission UI due to this port being random each time. + +A way around this is by adding another sidecar with a bash script to automate this process for you! It uses the [Gluetun Control Server](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md) and [Transmission RPC](https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md) to achieve this. + +The following sidecar container achieves this by: + +1. Fetching the SESSION ID from Transmission +1. Fetching the allocated port from Gluetun +1. Updating the Peer Port on Transmission via RPC +1. Repeating every two minutes + +```yaml +deployment: + ... + sideCarContainers: + ... + - name: 'update-peer-port' + image: 'alpine:latest' + command: + - '/bin/sh' + - '-c' + - | + # Add dependencies + echo "nameserver 8.8.8.8" > /etc/resolv.conf # allow quick resolution + apk update + apk add --no-cache jq curl + + # Transmission RPC URL + TRANSMISSION_URL="http://localhost:9091/transmission/rpc/" + # Gluetun Control Server URL + GLUETUN_URL="http://localhost:8000/v1" + # Initial Transmission RPC request with an invalid session ID to start + SESSION_ID="" + # How quickly to retry after a failure + ERROR_RETRY_S=60 + # How often to check for updated ports + UPDATE_CHECK_S=120 + + log() { + echo "[$(date)] $1" + } + + err() { + echo "[$(date)] [ERROR] $1" 1>&2 + } + + while true; do + echo "[$(date)] Fetching Transmission Session ID" + SESSION_ID=$(curl -s -X POST "$TRANSMISSION_URL" \ + -H "Content-Type: application/json" \ + -d "{\"arguments\":{\"peer-port\":$PORT},\"method\":\"session-set\"}" \ + | sed -n 's/.*X-Transmission-Session-Id: \([^<]*\).*/\1/p') + + if [ -z "$SESSION_ID" ]; then + err "Session ID not found. Trying again in ${ERROR_RETRY_S}s" + sleep $ERROR_RETRY_S + continue + fi + + log "Transmission Session ID: $SESSION_ID" + + while true; do + log 'Fetching port from Gluetun' + + PORT=$(curl -s "${GLUETUN_URL}/openvpn/portforwarded" | jq -r '.port') + + if [ -z "$PORT" ]; then + err "Port forward not found. Trying again in ${ERROR_RETRY_S}s" + sleep $ERROR_RETRY_S + continue + fi + + log "Gluetun forwarding Port: ${PORT}" + log "Sending request to Transmission RPC..." + + RESPONSE=$(curl -s -w "%{http_code}" -o /dev/null -X POST "$TRANSMISSION_URL" \ + -H "Content-Type: application/json" \ + -H "X-Transmission-Session-Id: $SESSION_ID" \ + -d "{\"arguments\":{\"peer-port\":$PORT},\"method\":\"session-set\"}") + + if [ "$RESPONSE" = "200" ]; then + log "Transmission RPC request successful. Peer port updated to $PORT." + elif [ "$RESPONSE" = "409" ]; then + err 'Received 409 Conflict. Fetching new Transmission session ID...' + break + else + err "Unexpected response: $RESPONSE from Transmission. Trying again in ${ERROR_RETRY_S}s" + sleep $ERROR_RETRY_S + continue + fi + + log "Updating again in ${UPDATE_CHECK_S}s" + sleep $UPDATE_CHECK_S + done + done +``` diff --git a/charts/transmission/values.yaml b/charts/transmission/values.yaml index 3788a27..9eb154f 100644 --- a/charts/transmission/values.yaml +++ b/charts/transmission/values.yaml @@ -168,19 +168,6 @@ persistentVolumeClaims: service: type: 'LoadBalancer' - ports: - - port: # use application.port by default - targetPort: 'http' - protocol: 'TCP' - name: 'http' - - port: 51413 - targetPort: 'peer' - protocol: 'TCP' - name: 'peer' - - port: 51413 - targetPort: 'peer-udp' - protocol: 'UDP' - name: 'peer-udp' ingress: enabled: true diff --git a/templates/deployment.yaml b/templates/deployment.yaml index 749449d..96e89e1 100644 --- a/templates/deployment.yaml +++ b/templates/deployment.yaml @@ -85,6 +85,9 @@ spec: {{- end }} {{- end }} containers: + {{- with .Values.deployment.sideCarContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} - name: '{{ .Chart.Name }}' securityContext: {{- toYaml .Values.deployment.container.securityContext | nindent 12 }} diff --git a/templates/service.yaml b/templates/service.yaml index a915abc..db1745b 100644 --- a/templates/service.yaml +++ b/templates/service.yaml @@ -13,8 +13,8 @@ spec: type: {{ .Values.service.type }} ports: {{- range .Values.deployment.container.ports }} - - port: {{ .containerPort | default $context.Values.application.port }} - targetPort: '{{ .targetPort }}' + - port: {{ if .servicePort }}{{ .servicePort }}{{ else if .containerPort }}{{ .containerPort }}{{ else }}{{ $context.Values.application.port }}{{ end }} + targetPort: {{ if .targetPort }}{{ .targetPort }}{{ else if .containerPort }}{{ .containerPort }}{{ else }}{{ $context.Values.application.port }}{{ end }} protocol: '{{ .protocol }}' name: '{{ .name }}' {{- end }} diff --git a/values.yaml b/values.yaml index 04f73c4..538e8e5 100644 --- a/values.yaml +++ b/values.yaml @@ -52,9 +52,11 @@ deployment: tag: '' pullPolicy: 'IfNotPresent' + # This also sets up a corresponding service ports: - name: 'http' containerPort: # By default uses application.port + servicePort: # By default uses application.port protocol: 'TCP' livenessProbe: @@ -81,6 +83,11 @@ deployment: # - name: 'foo' # mountPath: "/etc/foo" # readOnly: true + # + + # Any custom sidecar containers to run in a deployment + # for example, running a vpn container such as qdm12/gluetun for network traffic + sideCarContainers: [] volumes: {} # config: @@ -136,6 +143,7 @@ ingress: # hosts: # - chart-example.local +# For radarr, sonarr, readarr, lidarr metrics: enabled: false app: # Application to set up metrics for