From 24fa95525ab3f9424c8efedbca276dca4f744bd6 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:27:38 +0300 Subject: [PATCH 1/7] feat(templates): add NetworkPolicy template with ingress/egress rules Add comprehensive NetworkPolicy support for Jellyfin pod with: - Ingress rules: configurable external access or namespace/pod selectors - Egress rules: DNS resolution, metadata providers, custom rules - Automatic Prometheus integration when metrics are enabled - DLNA/hostNetwork detection with fail-safe error message - Support for restrictedEgress mode for high-security environments The NetworkPolicy is disabled by default to maintain backward compatibility. When enabled, it provides fine-grained control over network traffic: - Who can access Jellyfin (ingress) - What external connections Jellyfin can make (egress) NetworkPolicy requires CNI plugin support (Calico, Cilium, etc.) and cannot be used with hostNetwork mode (DLNA). The template includes validation to prevent misconfiguration. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/templates/networkpolicy.yaml | 109 +++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 charts/jellyfin/templates/networkpolicy.yaml diff --git a/charts/jellyfin/templates/networkpolicy.yaml b/charts/jellyfin/templates/networkpolicy.yaml new file mode 100644 index 0000000..5af3206 --- /dev/null +++ b/charts/jellyfin/templates/networkpolicy.yaml @@ -0,0 +1,109 @@ +{{- if .Values.networkPolicy.enabled }} +{{- if or .Values.jellyfin.enableDLNA .Values.podPrivileges.hostNetwork }} +{{- fail "NetworkPolicy cannot be enabled when hostNetwork is enabled (DLNA mode or podPrivileges.hostNetwork). NetworkPolicy does not apply to pods using hostNetwork. Please set networkPolicy.enabled=false or disable hostNetwork." }} +{{- end }} +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "jellyfin.fullname" . }} + labels: + {{- include "jellyfin.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "jellyfin.selectorLabels" . | nindent 6 }} + policyTypes: + {{- toYaml .Values.networkPolicy.policyTypes | nindent 4 }} + + {{- if has "Ingress" .Values.networkPolicy.policyTypes }} + ingress: + # Allow traffic to Jellyfin HTTP port + - ports: + - protocol: TCP + port: http + {{- if not .Values.networkPolicy.ingress.allowExternal }} + from: + {{- if or .Values.networkPolicy.ingress.podSelector .Values.networkPolicy.ingress.namespaceSelector }} + - podSelector: + {{- toYaml .Values.networkPolicy.ingress.podSelector | nindent 12 }} + {{- if .Values.networkPolicy.ingress.namespaceSelector }} + namespaceSelector: + {{- toYaml .Values.networkPolicy.ingress.namespaceSelector | nindent 12 }} + {{- end }} + {{- end }} + {{- end }} + + {{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} + # Allow Prometheus metrics scraping + - ports: + - protocol: TCP + port: {{ .Values.metrics.serviceMonitor.port }} + from: + - podSelector: + {{- toYaml .Values.networkPolicy.metrics.podSelector | nindent 12 }} + {{- if .Values.networkPolicy.metrics.namespace }} + namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Values.networkPolicy.metrics.namespace }} + {{- end }} + {{- end }} + + {{- with .Values.networkPolicy.ingress.customRules }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} + + {{- if has "Egress" .Values.networkPolicy.policyTypes }} + egress: + {{- if .Values.networkPolicy.egress.allowDNS }} + # Allow DNS resolution (required for Jellyfin operation) + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Values.networkPolicy.egress.dnsNamespace }} + podSelector: + matchLabels: + {{- toYaml .Values.networkPolicy.egress.dnsPodSelector | nindent 14 }} + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + {{- end }} + + {{- if .Values.networkPolicy.egress.allowAllEgress }} + # Allow all egress traffic (metadata providers, subtitles, images, etc.) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + {{- else }} + {{- if .Values.networkPolicy.egress.restrictedEgress.allowMetadata }} + # Allow HTTPS for metadata providers (TMDB, TheTVDB, OpenSubtitles, etc.) + - to: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - protocol: TCP + port: 443 + {{- end }} + + {{- if .Values.networkPolicy.egress.restrictedEgress.allowInCluster }} + # Allow pod-to-pod communication within the cluster + - to: + - podSelector: {} + {{- end }} + + {{- range .Values.networkPolicy.egress.restrictedEgress.allowedCIDRs }} + # Custom CIDR egress rule + - to: + - ipBlock: + cidr: {{ . }} + {{- end }} + {{- end }} + + {{- with .Values.networkPolicy.egress.customRules }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} From d2051f1dfecce3aa85153f741eb7d131e226bd4a Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:27:55 +0300 Subject: [PATCH 2/7] feat(values): add networkPolicy configuration section Add networkPolicy section to values.yaml with comprehensive configuration options: - enabled: boolean flag to enable/disable NetworkPolicy (default: false) - policyTypes: configure Ingress and/or Egress policies - ingress: control which pods/namespaces can access Jellyfin - allowExternal: permit access from any namespace (default: true) - podSelector/namespaceSelector: restrict access to specific pods/namespaces - customRules: advanced ingress rules for complex scenarios - egress: control outbound connections from Jellyfin - allowDNS: permit DNS resolution (default: true, required) - allowAllEgress: permit all internet access (default: true) - restrictedEgress: high-security mode with HTTPS-only metadata access - customRules: advanced egress rules - metrics: automatic Prometheus integration configuration All parameters include detailed documentation explaining: - Purpose and usage - Default values and recommended settings - Examples for common scenarios - Security implications The configuration supports use cases from simple deployments to zero-trust environments while maintaining sensible defaults. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/values.yaml | 149 +++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/charts/jellyfin/values.yaml b/charts/jellyfin/values.yaml index 068c0ef..2dc9aac 100644 --- a/charts/jellyfin/values.yaml +++ b/charts/jellyfin/values.yaml @@ -256,6 +256,153 @@ metrics: # -- additional init containers to run inside the pod. extraInitContainers: [] - + # -- additional sidecar containers to run inside the pod. extraContainers: [] + +# -- Network Policy configuration for network isolation and security. +# Requires a CNI plugin that supports NetworkPolicy (Calico, Cilium, Weave, etc.). +# WARNING: NetworkPolicy cannot be enabled when hostNetwork is used (DLNA mode). +# The chart will fail with an error if both are enabled simultaneously. +networkPolicy: + # -- Enable NetworkPolicy for the Jellyfin pod. + # By default, this is disabled to maintain backward compatibility. + # When enabled, you can control which pods can access Jellyfin (ingress) + # and what external connections Jellyfin can make (egress). + enabled: false + + # -- Policy types to enforce. Both ingress and egress policies can be enabled. + # See: https://kubernetes.io/docs/concepts/services-networking/network-policies/#policy-types + policyTypes: + - Ingress + - Egress + + # -- Ingress rules configuration - controls which pods/namespaces can access Jellyfin. + ingress: + # -- Allow external access from any namespace and any pod. + # When true, any pod in the cluster can access Jellyfin (default behavior). + # When false, only pods matching podSelector/namespaceSelector can access. + # Set to false for production environments to restrict access. + allowExternal: true + + # -- Custom pod selector for allowed ingress traffic. + # Only used when allowExternal is false. + # Allows you to specify which pods can access Jellyfin based on labels. + # Example - only allow pods with specific label: + # podSelector: + # matchLabels: + # jellyfin-client: "true" + podSelector: {} + + # -- Namespace selector to allow cross-namespace ingress. + # Only used when allowExternal is false. + # Allows you to specify which namespaces can access Jellyfin. + # Example - only allow from ingress-nginx namespace: + # namespaceSelector: + # matchLabels: + # name: ingress-nginx + namespaceSelector: {} + + # -- Additional custom ingress rules. + # Allows for complex scenarios not covered by the standard template. + # These rules are added as-is to the NetworkPolicy ingress section. + # Example - allow from monitoring namespace: + # customRules: + # - from: + # - namespaceSelector: + # matchLabels: + # name: monitoring + # ports: + # - protocol: TCP + # port: 8096 + customRules: [] + + # -- Egress rules configuration - controls what external connections Jellyfin can make. + egress: + # -- Allow DNS resolution (required for Jellyfin to function). + # This adds an egress rule for kube-system namespace with kube-dns pods. + # DNS is required for resolving metadata provider domains and subtitle services. + # It is highly recommended to keep this enabled. + allowDNS: true + + # -- DNS namespace where DNS service is running. + # Usually "kube-system" but can differ in some Kubernetes distributions. + dnsNamespace: kube-system + + # -- DNS pod selector labels. + # Default selector is for kube-dns, but CoreDNS or other DNS providers + # may use different labels. Adjust if needed. + # Common alternatives: + # k8s-app: kube-dns (default) + # k8s-app: coredns + # app.kubernetes.io/name: coredns + dnsPodSelector: + k8s-app: kube-dns + + # -- Allow all egress traffic (internet access for metadata, subtitles, images). + # When true, Jellyfin can connect to any external destination (0.0.0.0/0). + # This is the recommended default as Jellyfin needs internet access for: + # - Downloading movie/TV show metadata (TMDB, TheTVDB, OMDb) + # - Fetching poster images, fanart, and other artwork + # - Downloading subtitles (OpenSubtitles) + # - Updating plugins + # When false, you must configure restrictedEgress or customRules. + allowAllEgress: true + + # -- Restricted egress mode for security-conscious deployments. + # Only used when allowAllEgress is false. + # Provides fine-grained control over outbound connections. + restrictedEgress: + # -- Allow HTTPS (443/TCP) for metadata providers. + # Most metadata providers (TMDB, TheTVDB, OpenSubtitles, Fanart.tv) + # use HTTPS, so this covers the majority of use cases. + # This allows connections to any IP on port 443. + allowMetadata: true + + # -- Allow communication within the cluster (pod-to-pod). + # Useful if Jellyfin needs to connect to other services in the cluster. + # This allows connections to any pod in any namespace. + allowInCluster: true + + # -- Additional IP CIDR blocks to allow egress. + # Useful for allowing specific IP ranges for metadata providers + # or other external services. + # Example - allow entire internet except private networks: + # allowedCIDRs: + # - 0.0.0.0/0 + # Example - allow specific metadata provider IP ranges: + # allowedCIDRs: + # - 13.224.0.0/14 # CloudFront (used by many CDNs) + allowedCIDRs: [] + + # -- Additional custom egress rules. + # Allows for complex scenarios not covered by the standard template. + # These rules are added as-is to the NetworkPolicy egress section. + # Example - allow connections to specific database: + # customRules: + # - to: + # - podSelector: + # matchLabels: + # app: postgresql + # ports: + # - protocol: TCP + # port: 5432 + customRules: [] + + # -- Prometheus metrics scraping configuration. + # Automatically allows ingress from Prometheus when metrics.serviceMonitor.enabled is true. + # This ensures Prometheus can scrape metrics without additional configuration. + metrics: + # -- Namespace where Prometheus is running. + # Leave empty to use the same namespace as Jellyfin. + # Set to the monitoring namespace if Prometheus is in a different namespace. + # Example: "monitoring" or "prometheus" + namespace: "" + + # -- Pod selector for Prometheus pods. + # These labels must match your Prometheus deployment. + # The chart will automatically add an ingress rule for pods matching these labels. + # Default selector works with prometheus-operator and kube-prometheus-stack. + # Adjust if your Prometheus uses different labels. + podSelector: + app.kubernetes.io/name: prometheus From 32494dd1fdf787f77a83982a58038b4ae1b28b0c Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:28:13 +0300 Subject: [PATCH 3/7] feat(templates): add NOTES.txt with deployment status information Add NOTES.txt template that displays helpful information after deployment: - How to access Jellyfin (via Ingress or port-forward) - NetworkPolicy status and configuration summary when enabled - Ingress policy details (allowExternal, selectors, Prometheus) - Egress policy details (DNS, internet access, restrictions) - Warnings for misconfigurations (no selectors, DNS disabled) - Compatibility note about CNI requirements - Warning if NetworkPolicy + hostNetwork are both enabled The NOTES provide immediate visibility into the deployment configuration and help users quickly identify and troubleshoot network access issues. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/templates/NOTES.txt | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 charts/jellyfin/templates/NOTES.txt diff --git a/charts/jellyfin/templates/NOTES.txt b/charts/jellyfin/templates/NOTES.txt new file mode 100644 index 0000000..c07b044 --- /dev/null +++ b/charts/jellyfin/templates/NOTES.txt @@ -0,0 +1,88 @@ +Thank you for installing {{ .Chart.Name }}. + +Your release is named {{ .Release.Name }}. + +To learn more about the release, try: + + $ helm status {{ .Release.Name }} + $ helm get all {{ .Release.Name }} + +{{- if .Values.ingress.enabled }} + +Jellyfin is available via Ingress at: +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .host }}{{ (index .paths 0).path }} +{{- end }} +{{- else }} + +To access Jellyfin, you can use port-forwarding: + + $ kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include "jellyfin.fullname" . }} {{ .Values.service.port }}:{{ .Values.service.port }} + +Then visit http://localhost:{{ .Values.service.port }} in your browser. +{{- end }} + +{{- if .Values.networkPolicy.enabled }} + +Network Policy is ENABLED: +{{- if or .Values.jellyfin.enableDLNA .Values.podPrivileges.hostNetwork }} + WARNING: NetworkPolicy is incompatible with hostNetwork (DLNA mode). + The deployment will FAIL. Please disable networkPolicy.enabled or hostNetwork. +{{- else }} + + Ingress Policy: + {{- if .Values.networkPolicy.ingress.allowExternal }} + - ✓ Allowing connections from ANY pod in ANY namespace + {{- else }} + {{- if or .Values.networkPolicy.ingress.podSelector .Values.networkPolicy.ingress.namespaceSelector }} + - ✓ Allowing connections ONLY from pods matching: + {{- if .Values.networkPolicy.ingress.podSelector }} + Pod Labels: {{ .Values.networkPolicy.ingress.podSelector | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.networkPolicy.ingress.namespaceSelector }} + Namespace Labels: {{ .Values.networkPolicy.ingress.namespaceSelector | toYaml | nindent 8 }} + {{- end }} + {{- else }} + - ⚠ WARNING: allowExternal=false but no selectors defined - NO pods can connect! + {{- end }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} + - ✓ Allowing Prometheus metrics scraping from {{ .Values.networkPolicy.metrics.podSelector | toYaml }} + {{- end }} + {{- if .Values.networkPolicy.ingress.customRules }} + - ✓ Custom ingress rules applied ({{ len .Values.networkPolicy.ingress.customRules }} rules) + {{- end }} + + Egress Policy: + {{- if .Values.networkPolicy.egress.allowDNS }} + - ✓ DNS resolution allowed ({{ .Values.networkPolicy.egress.dnsNamespace }}/{{ .Values.networkPolicy.egress.dnsPodSelector | toYaml }}) + {{- else }} + - ⚠ WARNING: DNS is DISABLED - Jellyfin will NOT function properly! + {{- end }} + {{- if .Values.networkPolicy.egress.allowAllEgress }} + - ✓ All egress allowed (metadata, subtitles, images) + {{- else }} + {{- if .Values.networkPolicy.egress.restrictedEgress.allowMetadata }} + - ✓ HTTPS/443 allowed (metadata providers) + {{- end }} + {{- if .Values.networkPolicy.egress.restrictedEgress.allowInCluster }} + - ✓ In-cluster communication allowed + {{- end }} + {{- if .Values.networkPolicy.egress.restrictedEgress.allowedCIDRs }} + - ✓ Custom CIDRs allowed: {{ .Values.networkPolicy.egress.restrictedEgress.allowedCIDRs | join ", " }} + {{- end }} + {{- if not (or .Values.networkPolicy.egress.restrictedEgress.allowMetadata .Values.networkPolicy.egress.restrictedEgress.allowInCluster .Values.networkPolicy.egress.restrictedEgress.allowedCIDRs) }} + - ⚠ WARNING: No egress rules defined - Jellyfin cannot download metadata! + {{- end }} + {{- end }} + {{- if .Values.networkPolicy.egress.customRules }} + - ✓ Custom egress rules applied ({{ len .Values.networkPolicy.egress.customRules }} rules) + {{- end }} + + Note: NetworkPolicy requires a CNI plugin with NetworkPolicy support (Calico, Cilium, etc.) +{{- end }} +{{- else }} + +Network Policy is DISABLED (default). +To enable network isolation, set networkPolicy.enabled=true in your values. +{{- end }} From aaa9c91a8e644d5905e78b004cfbd3d72db4d437 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:28:38 +0300 Subject: [PATCH 4/7] test(networkpolicy): add comprehensive helm-unittest test suite Add 20+ unit tests for NetworkPolicy template covering: Creation conditions: - Not created by default (enabled: false) - Created when enabled: true - Fails with clear error when hostNetwork + NetworkPolicy enabled - Fails when DLNA + NetworkPolicy enabled Ingress rules: - Default allowExternal behavior (no 'from' restriction) - Restricted access with podSelector/namespaceSelector - Custom ingress rules application - Automatic Prometheus ingress when metrics enabled - Prometheus namespace selector when specified Egress rules: - DNS egress with default kube-system/kube-dns - Custom DNS namespace and pod selector - AllowAllEgress default behavior - Restricted egress modes (metadata, in-cluster) - Custom CIDR blocks - Custom egress rules application - DNS disabled scenario Policy configuration: - Correct policyTypes (Ingress, Egress) - Pod selector labels matching chart labels Tests ensure NetworkPolicy works correctly across all supported configurations and fails safely when misconfigured. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/tests/networkpolicy_test.yaml | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 charts/jellyfin/tests/networkpolicy_test.yaml diff --git a/charts/jellyfin/tests/networkpolicy_test.yaml b/charts/jellyfin/tests/networkpolicy_test.yaml new file mode 100644 index 0000000..1d54067 --- /dev/null +++ b/charts/jellyfin/tests/networkpolicy_test.yaml @@ -0,0 +1,297 @@ +suite: test networkpolicy +templates: + - networkpolicy.yaml +tests: + - it: should not create NetworkPolicy by default + asserts: + - hasDocuments: + count: 0 + + - it: should create NetworkPolicy when enabled + set: + networkPolicy.enabled: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: NetworkPolicy + - isAPIVersion: + of: networking.k8s.io/v1 + - equal: + path: metadata.name + value: RELEASE-NAME-jellyfin + + - it: should fail when NetworkPolicy enabled with DLNA + set: + networkPolicy.enabled: true + jellyfin.enableDLNA: true + asserts: + - failedTemplate: + errorMessage: "NetworkPolicy cannot be enabled when hostNetwork is enabled" + + - it: should fail when NetworkPolicy enabled with hostNetwork + set: + networkPolicy.enabled: true + podPrivileges.hostNetwork: true + asserts: + - failedTemplate: + errorMessage: "NetworkPolicy cannot be enabled when hostNetwork is enabled" + + - it: should have both Ingress and Egress policy types by default + set: + networkPolicy.enabled: true + asserts: + - contains: + path: spec.policyTypes + content: Ingress + - contains: + path: spec.policyTypes + content: Egress + + - it: should allow external ingress by default + set: + networkPolicy.enabled: true + asserts: + - isNotNull: + path: spec.ingress + # When allowExternal is true, there should be no 'from' in first ingress rule + - isNull: + path: spec.ingress[0].from + + - it: should restrict ingress when allowExternal is false + set: + networkPolicy.enabled: true + networkPolicy.ingress.allowExternal: false + networkPolicy.ingress.podSelector: + matchLabels: + app: test + asserts: + - isNotNull: + path: spec.ingress[0].from + - equal: + path: spec.ingress[0].from[0].podSelector.matchLabels.app + value: test + + - it: should add namespace selector for ingress when specified + set: + networkPolicy.enabled: true + networkPolicy.ingress.allowExternal: false + networkPolicy.ingress.namespaceSelector: + matchLabels: + name: frontend + asserts: + - equal: + path: spec.ingress[0].from[0].namespaceSelector.matchLabels.name + value: frontend + + - it: should create Prometheus ingress rule when metrics enabled + set: + networkPolicy.enabled: true + metrics.enabled: true + metrics.serviceMonitor.enabled: true + metrics.serviceMonitor.port: 8096 + asserts: + - contains: + path: spec.ingress + content: + ports: + - protocol: TCP + port: 8096 + from: + - podSelector: + app.kubernetes.io/name: prometheus + + - it: should add Prometheus namespace selector when specified + set: + networkPolicy.enabled: true + metrics.enabled: true + metrics.serviceMonitor.enabled: true + networkPolicy.metrics.namespace: monitoring + asserts: + - isNotNull: + path: spec.ingress[1].from[0].namespaceSelector + - equal: + path: spec.ingress[1].from[0].namespaceSelector.matchLabels.kubernetes.io/metadata.name + value: monitoring + + - it: should add custom ingress rules + set: + networkPolicy.enabled: true + networkPolicy.ingress.customRules: + - from: + - namespaceSelector: + matchLabels: + name: custom + ports: + - protocol: TCP + port: 9999 + asserts: + - contains: + path: spec.ingress + content: + from: + - namespaceSelector: + matchLabels: + name: custom + ports: + - protocol: TCP + port: 9999 + + - it: should allow DNS egress by default + set: + networkPolicy.enabled: true + asserts: + - contains: + path: spec.egress + content: + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + podSelector: + matchLabels: + k8s-app: kube-dns + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + + - it: should allow custom DNS namespace + set: + networkPolicy.enabled: true + networkPolicy.egress.dnsNamespace: custom-dns-ns + asserts: + - equal: + path: spec.egress[0].to[0].namespaceSelector.matchLabels.kubernetes.io/metadata.name + value: custom-dns-ns + + - it: should allow custom DNS pod selector + set: + networkPolicy.enabled: true + networkPolicy.egress.dnsPodSelector: + k8s-app: coredns + asserts: + - equal: + path: spec.egress[0].to[0].podSelector.matchLabels.k8s-app + value: coredns + + - it: should allow all egress by default + set: + networkPolicy.enabled: true + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 0.0.0.0/0 + + - it: should not allow all egress when allowAllEgress is false + set: + networkPolicy.enabled: true + networkPolicy.egress.allowAllEgress: false + asserts: + - notContains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 0.0.0.0/0 + + - it: should allow HTTPS when restrictedEgress.allowMetadata is true + set: + networkPolicy.enabled: true + networkPolicy.egress.allowAllEgress: false + networkPolicy.egress.restrictedEgress.allowMetadata: true + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 0.0.0.0/0 + ports: + - protocol: TCP + port: 443 + + - it: should allow in-cluster communication when restrictedEgress.allowInCluster is true + set: + networkPolicy.enabled: true + networkPolicy.egress.allowAllEgress: false + networkPolicy.egress.restrictedEgress.allowInCluster: true + asserts: + - contains: + path: spec.egress + content: + to: + - podSelector: {} + + - it: should add custom CIDR blocks + set: + networkPolicy.enabled: true + networkPolicy.egress.allowAllEgress: false + networkPolicy.egress.restrictedEgress.allowedCIDRs: + - 10.0.0.0/8 + - 192.168.0.0/16 + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 10.0.0.0/8 + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 192.168.0.0/16 + + - it: should add custom egress rules + set: + networkPolicy.enabled: true + networkPolicy.egress.customRules: + - to: + - podSelector: + matchLabels: + app: database + ports: + - protocol: TCP + port: 5432 + asserts: + - contains: + path: spec.egress + content: + to: + - podSelector: + matchLabels: + app: database + ports: + - protocol: TCP + port: 5432 + + - it: should not create DNS egress when allowDNS is false + set: + networkPolicy.enabled: true + networkPolicy.egress.allowDNS: false + asserts: + - notContains: + path: spec.egress + content: + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + + - it: should use correct pod selector labels + set: + networkPolicy.enabled: true + asserts: + - equal: + path: spec.podSelector.matchLabels.app\.kubernetes\.io/name + value: jellyfin + - equal: + path: spec.podSelector.matchLabels.app\.kubernetes\.io/instance + value: RELEASE-NAME From 5bfd6298488af5950f23bc42a065aa40c16ea1a2 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:28:57 +0300 Subject: [PATCH 5/7] docs(readme): add comprehensive NetworkPolicy documentation Add "Network Security" section to README.md.gotmpl with: Requirements: - CNI plugin support (Calico, Cilium, Weave, Canal) - DLNA incompatibility explanation Configuration examples: - Basic usage (default settings) - Production configuration (Ingress controller only) - High security (restricted egress) - Monitoring integration (Prometheus) - Advanced scenarios (multiple namespaces, custom rules) Security considerations: - Metadata provider requirements (TMDB, TheTVDB, OpenSubtitles) - DNS access importance - Local metadata alternative - Testing recommendations Troubleshooting guide: - Metadata download issues - Web interface access problems - Prometheus scraping failures - Deployment validation errors The documentation provides complete guidance for users to implement NetworkPolicy according to their security requirements, from basic isolation to zero-trust configurations. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/README.md.gotmpl | 201 +++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/charts/jellyfin/README.md.gotmpl b/charts/jellyfin/README.md.gotmpl index ee08a35..3c3c09f 100644 --- a/charts/jellyfin/README.md.gotmpl +++ b/charts/jellyfin/README.md.gotmpl @@ -85,3 +85,204 @@ extraVolumeMounts: - name: hwa mountPath: /dev/dri ``` + +## Network Security + +Jellyfin chart supports Kubernetes NetworkPolicy for network isolation and security hardening. NetworkPolicy allows you to control which pods can access Jellyfin (ingress) and what external connections Jellyfin can make (egress). + +### Requirements + +- **CNI Plugin**: NetworkPolicy requires a Container Network Interface (CNI) plugin that supports NetworkPolicies, such as: + - Calico + - Cilium + - Weave Net + - Canal + + Check with your cluster administrator if NetworkPolicy is supported in your cluster. + +- **DLNA Incompatibility**: NetworkPolicy cannot be enabled when `enableDLNA: true` or `podPrivileges.hostNetwork: true` is set, as pods using `hostNetwork` bypass NetworkPolicy rules. The chart will fail deployment with a clear error message if both are enabled. + +### Basic Usage + +By default, NetworkPolicy is disabled to maintain backward compatibility. To enable basic network isolation: + +```yaml +networkPolicy: + enabled: true +``` + +This will create a NetworkPolicy with the following defaults: +- **Ingress**: Allow connections from any pod in any namespace +- **Egress**: Allow DNS resolution and all internet access (required for metadata) + +### Production Configuration - Restrict to Ingress Controller + +For production environments, you typically want to restrict access to only allow traffic through the Ingress controller: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + namespaceSelector: + matchLabels: + name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + +ingress: + enabled: true + className: nginx + hosts: + - host: jellyfin.example.com + paths: + - path: / + pathType: Prefix +``` + +### High Security Configuration - Restricted Egress + +For security-conscious deployments that need to limit outbound connections: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + podSelector: + matchLabels: + jellyfin-client: "true" # Only pods with this label can access + egress: + allowDNS: true # Always needed + allowAllEgress: false # Block unrestricted internet + restrictedEgress: + allowMetadata: true # Allow HTTPS/443 for metadata providers (TMDB, etc.) + allowInCluster: false # Block pod-to-pod communication +``` + +**Note**: With this configuration, Jellyfin can only: +- Resolve DNS queries +- Connect to HTTPS (port 443) endpoints for metadata providers +- Cannot connect to other pods in the cluster +- Cannot access non-HTTPS services + +### Monitoring Integration + +If you're using Prometheus for monitoring, the chart automatically allows ingress from Prometheus pods when metrics are enabled: + +```yaml +networkPolicy: + enabled: true +metrics: + enabled: true + serviceMonitor: + enabled: true +``` + +By default, the chart allows ingress from pods with label `app.kubernetes.io/name: prometheus`. If your Prometheus uses different labels, customize the selector: + +```yaml +networkPolicy: + enabled: true + metrics: + namespace: monitoring # If Prometheus is in a different namespace + podSelector: + app: my-prometheus +``` + +### Advanced Configuration + +#### Multiple Namespaces Access + +Allow access from multiple namespaces using custom rules: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + customRules: + # Frontend namespace + - from: + - namespaceSelector: + matchLabels: + name: frontend + podSelector: + matchLabels: + access-jellyfin: "true" + ports: + - protocol: TCP + port: 8096 + + # Admin tools namespace + - from: + - namespaceSelector: + matchLabels: + name: admin-tools + ports: + - protocol: TCP + port: 8096 +``` + +#### Custom Egress Rules + +Allow connections to specific external services: + +```yaml +networkPolicy: + enabled: true + egress: + allowAllEgress: false + restrictedEgress: + allowMetadata: true + allowedCIDRs: + - 10.0.0.0/8 # Internal network + - 192.168.0.0/16 # Another internal network + customRules: + # Allow connection to external database + - to: + - ipBlock: + cidr: 203.0.113.0/24 + ports: + - protocol: TCP + port: 5432 +``` + +### Security Considerations + +1. **Metadata Providers**: Jellyfin requires internet access to download metadata (movie posters, descriptions, etc.) from: + - TheMovieDB (api.themoviedb.org) + - TheTVDB (api.thetvdb.com) + - OpenSubtitles (api.opensubtitles.com) + - Fanart.tv (fanart.tv) + + If you use `restrictedEgress.allowMetadata: true`, these will work as they all use HTTPS (port 443). + +2. **DNS Access**: DNS resolution is critical for Jellyfin operation. The chart prevents accidental DNS blocking by defaulting `allowDNS: true`. + +3. **Local Metadata**: If you want to completely block internet access, you can use local metadata (NFO files and local images). This requires manual setup and is not the default Jellyfin behavior. + +4. **Testing**: Always test NetworkPolicy changes in a development environment first. Misconfigured policies can block legitimate traffic. + +### Troubleshooting + +**Jellyfin can't download metadata/images:** +- Check that `egress.allowAllEgress: true` or `restrictedEgress.allowMetadata: true` is set +- Verify DNS egress is allowed: `egress.allowDNS: true` + +**Can't access Jellyfin web interface:** +- Verify ingress rules allow traffic from your access point (Ingress controller, LoadBalancer, etc.) +- Check NOTES.txt after deployment for detailed NetworkPolicy status + +**Prometheus can't scrape metrics:** +- Ensure `metrics.enabled: true` and `metrics.serviceMonitor.enabled: true` +- Verify `networkPolicy.metrics.podSelector` matches your Prometheus labels +- Set `networkPolicy.metrics.namespace` if Prometheus is in a different namespace + +**Deployment fails with "NetworkPolicy cannot be enabled...":** +- You have both `networkPolicy.enabled: true` and `hostNetwork: true` (or `enableDLNA: true`) +- NetworkPolicy doesn't work with host networking +- Either disable NetworkPolicy or disable host networking + +For more configuration options, see the full values documentation in [values.yaml](values.yaml). From a7cfada436877ca0d7e4a5c861f2d112a478ac02 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:29:18 +0300 Subject: [PATCH 6/7] chore(chart): bump version to 2.6.0 with NetworkPolicy support Bump chart version from 2.5.0 to 2.6.0 (minor version) for new feature. Added artifacthub.io/changes annotations: - NetworkPolicy support for network isolation and security hardening - NOTES.txt with deployment status and configuration summary This is a backward-compatible change: - NetworkPolicy is disabled by default - Existing installations will not be affected - Users can opt-in to NetworkPolicy by setting networkPolicy.enabled=true The NetworkPolicy feature provides enterprise-grade network security controls for Jellyfin deployments in Kubernetes environments with CNI plugin support. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/Chart.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/charts/jellyfin/Chart.yaml b/charts/jellyfin/Chart.yaml index 96a351b..a742864 100644 --- a/charts/jellyfin/Chart.yaml +++ b/charts/jellyfin/Chart.yaml @@ -8,5 +8,14 @@ keywords: - jellyfin - media - self-hosted -version: 2.5.0 +version: 2.6.0 appVersion: 10.11.2 +annotations: + artifacthub.io/changes: | + - kind: added + description: "Add NetworkPolicy support for network isolation and security hardening" + links: + - name: Documentation + url: https://kubernetes.io/docs/concepts/services-networking/network-policies/ + - kind: added + description: "Add NOTES.txt with deployment status and NetworkPolicy configuration summary" From 5be9dae42087097c23cd2d18e766e3f576def518 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 7 Nov 2025 19:30:56 +0300 Subject: [PATCH 7/7] docs(readme): regenerate README.md from template with helm-docs Regenerate README.md from README.md.gotmpl template using helm-docs. This adds the complete NetworkPolicy documentation section and updates the values table with all networkPolicy configuration parameters. The generated documentation includes: - Updated version badge (2.6.0) - Complete networkPolicy values table with descriptions - Network Security section with examples and troubleshooting - All parameters from values.yaml with auto-generated descriptions Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- charts/jellyfin/README.md | 224 +++++++++++++++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 1 deletion(-) diff --git a/charts/jellyfin/README.md b/charts/jellyfin/README.md index 10952b6..323c8b1 100644 --- a/charts/jellyfin/README.md +++ b/charts/jellyfin/README.md @@ -1,6 +1,6 @@ # jellyfin -![Version: 2.5.0](https://img.shields.io/badge/Version-2.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 10.11.2](https://img.shields.io/badge/AppVersion-10.11.2-informational?style=flat-square) +![Version: 2.6.0](https://img.shields.io/badge/Version-2.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 10.11.2](https://img.shields.io/badge/AppVersion-10.11.2-informational?style=flat-square) A Helm chart for Jellyfin Media Server @@ -86,6 +86,27 @@ helm install my-jellyfin jellyfin/jellyfin -f values.yaml | metrics.serviceMonitor.targetLabels | list | `[]` | Target labels to add to the scraped metrics | | metrics.serviceMonitor.tlsConfig | object | `{}` | TLS configuration for scraping metrics | | nameOverride | string | `""` | Override the default name of the chart. | +| networkPolicy | object | `{"egress":{"allowAllEgress":true,"allowDNS":true,"customRules":[],"dnsNamespace":"kube-system","dnsPodSelector":{"k8s-app":"kube-dns"},"restrictedEgress":{"allowInCluster":true,"allowMetadata":true,"allowedCIDRs":[]}},"enabled":false,"ingress":{"allowExternal":true,"customRules":[],"namespaceSelector":{},"podSelector":{}},"metrics":{"namespace":"","podSelector":{"app.kubernetes.io/name":"prometheus"}},"policyTypes":["Ingress","Egress"]}` | Network Policy configuration for network isolation and security. Requires a CNI plugin that supports NetworkPolicy (Calico, Cilium, Weave, etc.). WARNING: NetworkPolicy cannot be enabled when hostNetwork is used (DLNA mode). The chart will fail with an error if both are enabled simultaneously. | +| networkPolicy.egress | object | `{"allowAllEgress":true,"allowDNS":true,"customRules":[],"dnsNamespace":"kube-system","dnsPodSelector":{"k8s-app":"kube-dns"},"restrictedEgress":{"allowInCluster":true,"allowMetadata":true,"allowedCIDRs":[]}}` | Egress rules configuration - controls what external connections Jellyfin can make. | +| networkPolicy.egress.allowAllEgress | bool | `true` | Allow all egress traffic (internet access for metadata, subtitles, images). When true, Jellyfin can connect to any external destination (0.0.0.0/0). This is the recommended default as Jellyfin needs internet access for: - Downloading movie/TV show metadata (TMDB, TheTVDB, OMDb) - Fetching poster images, fanart, and other artwork - Downloading subtitles (OpenSubtitles) - Updating plugins When false, you must configure restrictedEgress or customRules. | +| networkPolicy.egress.allowDNS | bool | `true` | Allow DNS resolution (required for Jellyfin to function). This adds an egress rule for kube-system namespace with kube-dns pods. DNS is required for resolving metadata provider domains and subtitle services. It is highly recommended to keep this enabled. | +| networkPolicy.egress.customRules | list | `[]` | Additional custom egress rules. Allows for complex scenarios not covered by the standard template. These rules are added as-is to the NetworkPolicy egress section. Example - allow connections to specific database: customRules: - to: - podSelector: matchLabels: app: postgresql ports: - protocol: TCP port: 5432 | +| networkPolicy.egress.dnsNamespace | string | `"kube-system"` | DNS namespace where DNS service is running. Usually "kube-system" but can differ in some Kubernetes distributions. | +| networkPolicy.egress.dnsPodSelector | object | `{"k8s-app":"kube-dns"}` | DNS pod selector labels. Default selector is for kube-dns, but CoreDNS or other DNS providers may use different labels. Adjust if needed. Common alternatives: k8s-app: kube-dns (default) k8s-app: coredns app.kubernetes.io/name: coredns | +| networkPolicy.egress.restrictedEgress | object | `{"allowInCluster":true,"allowMetadata":true,"allowedCIDRs":[]}` | Restricted egress mode for security-conscious deployments. Only used when allowAllEgress is false. Provides fine-grained control over outbound connections. | +| networkPolicy.egress.restrictedEgress.allowInCluster | bool | `true` | Allow communication within the cluster (pod-to-pod). Useful if Jellyfin needs to connect to other services in the cluster. This allows connections to any pod in any namespace. | +| networkPolicy.egress.restrictedEgress.allowMetadata | bool | `true` | Allow HTTPS (443/TCP) for metadata providers. Most metadata providers (TMDB, TheTVDB, OpenSubtitles, Fanart.tv) use HTTPS, so this covers the majority of use cases. This allows connections to any IP on port 443. | +| networkPolicy.egress.restrictedEgress.allowedCIDRs | list | `[]` | Additional IP CIDR blocks to allow egress. Useful for allowing specific IP ranges for metadata providers or other external services. Example - allow entire internet except private networks: allowedCIDRs: - 0.0.0.0/0 Example - allow specific metadata provider IP ranges: allowedCIDRs: - 13.224.0.0/14 # CloudFront (used by many CDNs) | +| networkPolicy.enabled | bool | `false` | Enable NetworkPolicy for the Jellyfin pod. By default, this is disabled to maintain backward compatibility. When enabled, you can control which pods can access Jellyfin (ingress) and what external connections Jellyfin can make (egress). | +| networkPolicy.ingress | object | `{"allowExternal":true,"customRules":[],"namespaceSelector":{},"podSelector":{}}` | Ingress rules configuration - controls which pods/namespaces can access Jellyfin. | +| networkPolicy.ingress.allowExternal | bool | `true` | Allow external access from any namespace and any pod. When true, any pod in the cluster can access Jellyfin (default behavior). When false, only pods matching podSelector/namespaceSelector can access. Set to false for production environments to restrict access. | +| networkPolicy.ingress.customRules | list | `[]` | Additional custom ingress rules. Allows for complex scenarios not covered by the standard template. These rules are added as-is to the NetworkPolicy ingress section. Example - allow from monitoring namespace: customRules: - from: - namespaceSelector: matchLabels: name: monitoring ports: - protocol: TCP port: 8096 | +| networkPolicy.ingress.namespaceSelector | object | `{}` | Namespace selector to allow cross-namespace ingress. Only used when allowExternal is false. Allows you to specify which namespaces can access Jellyfin. Example - only allow from ingress-nginx namespace: namespaceSelector: matchLabels: name: ingress-nginx | +| networkPolicy.ingress.podSelector | object | `{}` | Custom pod selector for allowed ingress traffic. Only used when allowExternal is false. Allows you to specify which pods can access Jellyfin based on labels. Example - only allow pods with specific label: podSelector: matchLabels: jellyfin-client: "true" | +| networkPolicy.metrics | object | `{"namespace":"","podSelector":{"app.kubernetes.io/name":"prometheus"}}` | Prometheus metrics scraping configuration. Automatically allows ingress from Prometheus when metrics.serviceMonitor.enabled is true. This ensures Prometheus can scrape metrics without additional configuration. | +| networkPolicy.metrics.namespace | string | `""` | Namespace where Prometheus is running. Leave empty to use the same namespace as Jellyfin. Set to the monitoring namespace if Prometheus is in a different namespace. Example: "monitoring" or "prometheus" | +| networkPolicy.metrics.podSelector | object | `{"app.kubernetes.io/name":"prometheus"}` | Pod selector for Prometheus pods. These labels must match your Prometheus deployment. The chart will automatically add an ingress rule for pods matching these labels. Default selector works with prometheus-operator and kube-prometheus-stack. Adjust if your Prometheus uses different labels. | +| networkPolicy.policyTypes | list | `["Ingress","Egress"]` | Policy types to enforce. Both ingress and egress policies can be enabled. See: https://kubernetes.io/docs/concepts/services-networking/network-policies/#policy-types | | nodeSelector | object | `{}` | Node selector for pod scheduling. | | persistence.config.accessMode | string | `"ReadWriteOnce"` | | | persistence.config.annotations | object | `{}` | Custom annotations to be added to the PVC | @@ -160,3 +181,204 @@ extraVolumeMounts: - name: hwa mountPath: /dev/dri ``` + +## Network Security + +Jellyfin chart supports Kubernetes NetworkPolicy for network isolation and security hardening. NetworkPolicy allows you to control which pods can access Jellyfin (ingress) and what external connections Jellyfin can make (egress). + +### Requirements + +- **CNI Plugin**: NetworkPolicy requires a Container Network Interface (CNI) plugin that supports NetworkPolicies, such as: + - Calico + - Cilium + - Weave Net + - Canal + + Check with your cluster administrator if NetworkPolicy is supported in your cluster. + +- **DLNA Incompatibility**: NetworkPolicy cannot be enabled when `enableDLNA: true` or `podPrivileges.hostNetwork: true` is set, as pods using `hostNetwork` bypass NetworkPolicy rules. The chart will fail deployment with a clear error message if both are enabled. + +### Basic Usage + +By default, NetworkPolicy is disabled to maintain backward compatibility. To enable basic network isolation: + +```yaml +networkPolicy: + enabled: true +``` + +This will create a NetworkPolicy with the following defaults: +- **Ingress**: Allow connections from any pod in any namespace +- **Egress**: Allow DNS resolution and all internet access (required for metadata) + +### Production Configuration - Restrict to Ingress Controller + +For production environments, you typically want to restrict access to only allow traffic through the Ingress controller: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + namespaceSelector: + matchLabels: + name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + +ingress: + enabled: true + className: nginx + hosts: + - host: jellyfin.example.com + paths: + - path: / + pathType: Prefix +``` + +### High Security Configuration - Restricted Egress + +For security-conscious deployments that need to limit outbound connections: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + podSelector: + matchLabels: + jellyfin-client: "true" # Only pods with this label can access + egress: + allowDNS: true # Always needed + allowAllEgress: false # Block unrestricted internet + restrictedEgress: + allowMetadata: true # Allow HTTPS/443 for metadata providers (TMDB, etc.) + allowInCluster: false # Block pod-to-pod communication +``` + +**Note**: With this configuration, Jellyfin can only: +- Resolve DNS queries +- Connect to HTTPS (port 443) endpoints for metadata providers +- Cannot connect to other pods in the cluster +- Cannot access non-HTTPS services + +### Monitoring Integration + +If you're using Prometheus for monitoring, the chart automatically allows ingress from Prometheus pods when metrics are enabled: + +```yaml +networkPolicy: + enabled: true +metrics: + enabled: true + serviceMonitor: + enabled: true +``` + +By default, the chart allows ingress from pods with label `app.kubernetes.io/name: prometheus`. If your Prometheus uses different labels, customize the selector: + +```yaml +networkPolicy: + enabled: true + metrics: + namespace: monitoring # If Prometheus is in a different namespace + podSelector: + app: my-prometheus +``` + +### Advanced Configuration + +#### Multiple Namespaces Access + +Allow access from multiple namespaces using custom rules: + +```yaml +networkPolicy: + enabled: true + ingress: + allowExternal: false + customRules: + # Frontend namespace + - from: + - namespaceSelector: + matchLabels: + name: frontend + podSelector: + matchLabels: + access-jellyfin: "true" + ports: + - protocol: TCP + port: 8096 + + # Admin tools namespace + - from: + - namespaceSelector: + matchLabels: + name: admin-tools + ports: + - protocol: TCP + port: 8096 +``` + +#### Custom Egress Rules + +Allow connections to specific external services: + +```yaml +networkPolicy: + enabled: true + egress: + allowAllEgress: false + restrictedEgress: + allowMetadata: true + allowedCIDRs: + - 10.0.0.0/8 # Internal network + - 192.168.0.0/16 # Another internal network + customRules: + # Allow connection to external database + - to: + - ipBlock: + cidr: 203.0.113.0/24 + ports: + - protocol: TCP + port: 5432 +``` + +### Security Considerations + +1. **Metadata Providers**: Jellyfin requires internet access to download metadata (movie posters, descriptions, etc.) from: + - TheMovieDB (api.themoviedb.org) + - TheTVDB (api.thetvdb.com) + - OpenSubtitles (api.opensubtitles.com) + - Fanart.tv (fanart.tv) + + If you use `restrictedEgress.allowMetadata: true`, these will work as they all use HTTPS (port 443). + +2. **DNS Access**: DNS resolution is critical for Jellyfin operation. The chart prevents accidental DNS blocking by defaulting `allowDNS: true`. + +3. **Local Metadata**: If you want to completely block internet access, you can use local metadata (NFO files and local images). This requires manual setup and is not the default Jellyfin behavior. + +4. **Testing**: Always test NetworkPolicy changes in a development environment first. Misconfigured policies can block legitimate traffic. + +### Troubleshooting + +**Jellyfin can't download metadata/images:** +- Check that `egress.allowAllEgress: true` or `restrictedEgress.allowMetadata: true` is set +- Verify DNS egress is allowed: `egress.allowDNS: true` + +**Can't access Jellyfin web interface:** +- Verify ingress rules allow traffic from your access point (Ingress controller, LoadBalancer, etc.) +- Check NOTES.txt after deployment for detailed NetworkPolicy status + +**Prometheus can't scrape metrics:** +- Ensure `metrics.enabled: true` and `metrics.serviceMonitor.enabled: true` +- Verify `networkPolicy.metrics.podSelector` matches your Prometheus labels +- Set `networkPolicy.metrics.namespace` if Prometheus is in a different namespace + +**Deployment fails with "NetworkPolicy cannot be enabled...":** +- You have both `networkPolicy.enabled: true` and `hostNetwork: true` (or `enableDLNA: true`) +- NetworkPolicy doesn't work with host networking +- Either disable NetworkPolicy or disable host networking + +For more configuration options, see the full values documentation in [values.yaml](values.yaml).