𝔩𝔢𝔩𝕠𝔭𝔢𝔷
Theme

Homelab

Longhorn Hardening

Hardening Longhorn: Pinned Version, Encryption, and NetworkPolicy

Overview

This article hardens Longhorn by pinning the Helm chart version, enabling encryption, and adding NetworkPolicy. Physical disk theft or node compromise currently exposes all persistent data in plaintext - we'll enable the encryption infrastructure here, then migrate existing volumes in each application's hardening article.

Tip:Having trouble? See v1.7.0 for what your setup should look like after completing this article.

Before You Begin

Prerequisites

What We're Hardening

ComponentBeforeAfter
Helm chart1.7.x (floating)Pinned version
EncryptionNoneInfrastructure enabled
NetworkPolicyNoneEgress limited

Why These Controls

Helm Pinning: Floating versions can introduce breaking changes or vulnerabilities without notice. Pinning ensures reproducible deployments.

Encryption: Longhorn uses LUKS encryption for volumes. Without it, anyone with physical disk access can read all data. Encryption protects data at rest.

NetworkPolicy: Longhorn components need to communicate with each other and the Kubernetes API. We can limit egress to only required destinations.

Note:Encryption applies to NEW volumes only. Existing volumes are migrated in each application's hardening article (Phase 4).

Harden Longhorn

Check: Current Helm Version

helm list -n longhorn-system

Note the chart version (e.g., 1.7.3) - this is what you'll pin.

HelmRelease: Pin Chart Version

k8s/core/longhorn/helmrelease.yaml:

# ... existing header ...
spec:
    interval: 1h
    chart:
        spec:
            chart: longhorn
            version: "1.7.3" # CHANGE from "1.7.x"
            sourceRef:
                kind: HelmRepository
                name: longhorn
                namespace: flux-system
    # ... existing install, upgrade, values ...

Update the version to match your current deployment if different.

Secret: Encryption Key

Create a secret with the encryption passphrase. Generate a strong passphrase and store it in your password manager.

k8s/core/longhorn/secret.sops.yaml:

---
apiVersion: v1
kind: Secret
metadata:
    name: longhorn-crypto
    namespace: longhorn-system
type: Opaque
stringData:
    CRYPTO_KEY_VALUE: "<your-encryption-passphrase>"
    CRYPTO_KEY_PROVIDER: "secret"
    CRYPTO_KEY_CIPHER: "aes-xts-plain64"
    CRYPTO_KEY_HASH: "sha256"
    CRYPTO_KEY_SIZE: "256"
    CRYPTO_PBKDF: "argon2i"

Encrypt the secret:

sops -e -i k8s/core/longhorn/secret.sops.yaml

StorageClass: Encrypted Volumes

k8s/core/longhorn/storageclass-encrypted.yaml:

---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: longhorn-encrypted
provisioner: driver.longhorn.io
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: Immediate
parameters:
    numberOfReplicas: "2"
    staleReplicaTimeout: "2880"
    fromBackup: ""
    encrypted: "true"
    csi.storage.k8s.io/provisioner-secret-name: "longhorn-crypto"
    csi.storage.k8s.io/provisioner-secret-namespace: "longhorn-system"
    csi.storage.k8s.io/node-publish-secret-name: "longhorn-crypto"
    csi.storage.k8s.io/node-publish-secret-namespace: "longhorn-system"
    csi.storage.k8s.io/node-stage-secret-name: "longhorn-crypto"
    csi.storage.k8s.io/node-stage-secret-namespace: "longhorn-system"

NetworkPolicy: Restrict Longhorn Egress

k8s/core/longhorn/networkpolicy.yaml:

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
    name: longhorn
    namespace: longhorn-system
spec:
    podSelector: {}
    policyTypes:
        - Egress
    egress:
        # DNS resolution
        - to:
              - namespaceSelector:
                    matchLabels:
                        kubernetes.io/metadata.name: kube-system
          ports:
              - protocol: UDP
                port: 53
        # Kubernetes API
        - to:
              - ipBlock:
                    cidr: 192.168.10.30/32
              - ipBlock:
                    cidr: 192.168.10.31/32
              - ipBlock:
                    cidr: 192.168.10.32/32
          ports:
              - protocol: TCP
                port: 6443
        # Lab VLAN (node communication for replica sync)
        - to:
              - ipBlock:
                    cidr: 192.168.10.0/24

Kustomization: Add Encryption and Policy

k8s/core/longhorn/kustomization.yaml:

# ... existing header ...
resources:
    - namespace.yaml
    - helmrepository.yaml
    - helmrelease.yaml
    - secret.sops.yaml # ADD
    - storageclass-encrypted.yaml # ADD
    - networkpolicy.yaml # ADD

Deploy Changes

Git: Commit Encryption and Policy

git add k8s/core/longhorn/
git commit -m "feat(longhorn): harden with encryption and NetworkPolicy"
git push

Flux: Sync Encryption and Policy

flux reconcile source git flux-system
flux reconcile kustomization sync

Verify Hardening

Verify: Pinned Helm Version

kubectl get helmrelease -n longhorn-system longhorn -o jsonpath='{.spec.chart.spec.version}'

Expected: Your pinned version. HelmChart reconciliation can take a minute.

Verify: Encryption Resources

kubectl get secret -n longhorn-system longhorn-crypto
kubectl get storageclass longhorn-encrypted

Expected: Both resources exist.

Verify: NetworkPolicy Applied

kubectl get networkpolicy -n longhorn-system

Expected: longhorn policy listed.

Next Steps

Important:If you're on Longhorn 1.7.x (from the v2 series), upgrade before creating encrypted volumes. Encryption fails on Talos 1.9+ without Longhorn 1.9.x.

See: Longhorn Upgrade

With Longhorn hardened and upgraded, continue with application hardening.

See: Minecraft Hardening

Previous
MetalLB & Ingress Hardening