Skip to main content

Restricting Redis Network Access

The Redis instance bundled with Unstract has no password set and accepts connections on its port from any pod in the cluster, not only from Unstract's own pods. Restricting it to the namespace Unstract is installed in is recommended.

What this involves

This is a values change, not a version upgrade. You apply it by re-running helm upgrade at the chart version you are already on, and it alters only a NetworkPolicy object — no pods restart and there is no downtime.

Why this matters

REPLICAOF (previously SLAVEOF) is an ordinary Redis command, not a privileged one, and the bundled Redis requires no authentication. Any workload that can open a socket to the Redis port can therefore tell it to become a replica of a different Redis instance.

When that happens, Redis:

  • turns read-only, so writes from Unstract begin to fail; and
  • loses its data. Redis discards its existing dataset once a replacement has been transferred from the other instance, immediately before loading it — so even a synchronisation that transfers successfully but then fails to load leaves the original data already gone. If the other instance keeps re-asserting the relationship, this repeats.

This is a realistic risk on a shared cluster that runs more than one Redis. Redis Sentinel in particular identifies the instances it manages by IP address, and Kubernetes reuses pod IPs. If a Sentinel-managed replica elsewhere in the cluster is deleted and its IP is later assigned to the Unstract Redis pod, Sentinel can reconfigure your Redis — with nobody having changed any configuration, and with no error raised by the deployment that caused it.

Does this apply to my cluster?

Two conditions have to hold. Check both.

1. Is there another Redis or Sentinel in the cluster?

kubectl get pods -A -l app.kubernetes.io/name=redis

Anything listed outside your Unstract namespace is a potential source, and Sentinel-managed deployments are the ones that re-assert replication automatically.

An empty result is not proof there is none. That label is a Helm chart convention, so this finds Redis deployed by the common charts but misses one installed under a different name, or managed by a Redis operator — and operator-managed Sentinel is exactly the kind that re-asserts replication. If you are unsure what else runs in the cluster, treat condition 2 as the deciding one.

If you also run LLMWhisperer on-prem, note that it ships its own Sentinel-managed Redis, which is unauthenticated by default in the same way. It needs the equivalent restriction applied to its own chart.

2. Does your cluster enforce NetworkPolicy?

Where it does not, the policy is accepted by the Kubernetes API server and silently ignored — it will not break anything, but it will not protect anything either.

PlatformNetworkPolicy enforced?
GKE with Dataplane V2Yes
GKE Standard, with neither Dataplane V2 nor the Calico add-onNo
EKS with the default VPC CNIOnly with VPC CNI 1.14+ and enableNetworkPolicy turned on
AKS with Azure NPM, Calico, or CiliumYes
AKS with no network policy engine selectedNo
OpenShift (OVN-Kubernetes)Yes
kubeadm with FlannelNo

If your cluster does not enforce NetworkPolicy, we recommend enabling enforcement — it is a cluster-level capability that protects every workload, not only Redis. On the managed platforms this can usually be done on an existing cluster:

# GKE Standard — enable the Calico add-on, then enforcement.
# Use --zone instead of --region for a zonal cluster.
gcloud container clusters update CLUSTER_NAME --region REGION --update-addons=NetworkPolicy=ENABLED
gcloud container clusters update CLUSTER_NAME --region REGION --enable-network-policy

# AKS — install Azure NPM or Calico on an existing cluster.
az aks update --name CLUSTER_NAME --resource-group RESOURCE_GROUP --network-policy calico
Both are disruptive

The GKE commands recreate your node pools. The AKS command reimages nodes, and enforcement only begins once every node has been reimaged — so treat either as a maintenance window, not a control-plane change.

Moving AKS to the Cilium data plane (az aks update --network-dataplane cilium) is also an option, but it reimages all node pools simultaneously and is not supported directly from kubenet — a kubenet cluster must move to Azure CNI Overlay first. Azure NPM or Calico is usually the smaller step.

Where enforcement genuinely cannot be enabled, isolate Redis by other means — for example a dedicated cluster or node pool for Unstract, or a service mesh with mTLS.

Apply the restriction

Apply both settings together

allowExternal: false must not be used on its own. Alone, it restricts Redis to pods carrying a client label (unstract-platform-redis-client in a standard install) plus Redis's own pods — and no Unstract service sets that label, so every service would immediately lose access to Redis. The extraIngress block is what restores access for the namespace. Copy the whole block below.

Add the following to your on-prem.values.yaml:

redis:
networkPolicy:
enabled: true
allowExternal: false
extraIngress:
- ports:
- port: 6379
- port: 26379
from:
- podSelector: {}

podSelector: {} matches every pod in the namespace the policy is installed in, so no pod needs a label and the same setting is correct in any namespace. Port 6379 is Redis itself and 26379 is Sentinel — a standalone deployment does not listen on 26379, and including it means the setting still works if you later move to High Availability, which uses Sentinel.

Then apply it by running your usual helm upgrade — the same command, the same values files, and the same --version you are already on. The only thing that changes is the contents of on-prem.values.yaml.

Do not drop any values file

helm upgrade renders only from what is on the command line, so any values file you leave out reverts everything it configures to chart defaults. On an HA deployment, omitting values-multi-az.yaml replaces the three-node Redis and the MinIO Operator tenant with single-replica standalone equivalents.

This is also not a version upgrade — keep --version at your current release. Run helm list -n <namespace> to confirm what that is.

Verify

Resource names differ between the standard and HA configurations, and so does how the replication check below is read. Determine which you are running first:

kubectl get pods -n <namespace> -l app.kubernetes.io/name=redis \
-o custom-columns='POD:.metadata.name,CONTAINERS:.spec.containers[*].name'

Read the CONTAINERS column — that is the deciding signal, and it does not depend on how many replicas you run or on your Helm release name.

CONTAINERS columnConfigurationNetworkPolicyRedis pod(s)
redis onlyStandard (default values)unstract-platform-redisunstract-platform-redis-master-0
redis,sentinelHA (values-multi-az.yaml)unstract-platform-redis-masterunstract-platform-redis-master-node-0, -1, -2

The Redis service is unstract-platform-redis-master on both.

If you also run LLMWhisperer in the same namespace, its Redis appears here too — its pods are named redis-node-*, and the rows above apply only to the unstract-platform-redis-* pods.

# 1. The policy now restricts sources — expect a `from:` block, not just `ports:`
kubectl get networkpolicy -n <namespace> <networkpolicy-name> -o yaml

# 2. Unstract can still reach Redis
kubectl exec -n <namespace> deploy/unstract-backend -- \
sh -c 'timeout 8 python -c "import socket;socket.create_connection((\"unstract-platform-redis-master\",6379),5);print(\"OK\")"'

Those two confirm the change did what it should — sources are now restricted, and your services still have access. Both apply to either configuration.

Check whether this instance was taken over

On a standard deployment this is unambiguous. A standard deployment runs a single Redis, and nothing in this chart's documented configurations makes it a replica:

kubectl exec -n <namespace> unstract-platform-redis-master-0 -c redis -- \
redis-cli INFO replication | grep '^role'

role:master is healthy and is the only correct result here. On a standard deployment, role:slave means another Redis has taken this instance over — see below. Do not draw that conclusion on an HA deployment; see the warning immediately below.

On an HA deployment, do not judge this from the role

Under values-multi-az.yaml, two of the three nodes are replicas at all times by design, so role:slave is the normal healthy reading and tells you nothing on its own.

We deliberately do not publish a self-check or a self-remediation for the HA case, for two separate reasons:

  • The remediation on this page is wrong for HA regardless of what any check says. REPLICAOF NO ONE detaches a node from the Sentinels that manage it, which then fight to reattach it. The correct fix on HA is to remove the foreign Redis and let your own Sentinels re-converge — not to reconfigure a node by hand.
  • A healthy-looking reading cannot be trusted to rule a problem out. While a takeover is in progress the competing Sentinel quorums move the affected node back and forth every few seconds, so role and master_host sampled once may show a perfectly normal result moments before showing an abnormal one. A single clean reading is not evidence that nothing is wrong.

If you have reason to believe an HA deployment is affected — unexplained cache or session loss, or another Redis in the cluster that you do not control — contact Unstract support rather than running any remediation from this page.

Roll back

Remove the redis.networkPolicy block you added, or set allowExternal: true, and re-run the same helm upgrade. This restores the previous behaviour immediately; no pods restart.

If Redis has already been taken over

Standard deployments only

Everything in this section applies only where the check above printed role:slave on a standard deployment.

Do not run it on an HA deployment. There, role:slave is the healthy steady state, and REPLICAOF NO ONE on a healthy replica detaches it from its own Sentinel — turning a working cluster into an outage. For HA, contact Unstract support.

1. Identify the source

kubectl exec -n <namespace> unstract-platform-redis-master-0 -c redis -- \
redis-cli INFO replication | grep -E '^(role|master_host|master_link_status)'

master_host is the instance yours is following. It is usually a bare IP address, because the takeover this page describes is performed by a Redis Sentinel, and Sentinel instructs a replica using an address rather than a name. Resolve it to find the workload responsible:

kubectl get pods -A -o wide | grep -w <the-ip>
ResultMeaning
A pod in another namespaceThat is the source.
No pod matchesThe address belonged to a pod that has since been deleted. Its Redis may still exist under a new address — look for other Redis deployments in the cluster: kubectl get pods -A -l app.kubernetes.io/name=redis.
A pod in your own namespaceUnexpected on a standard deployment. Do not continue; contact Unstract support.

2. Remove the source, then restore this instance

Stop or remove the other Redis first — while it is running it will re-assert the relationship within seconds, and the command below will be undone before you can verify it.

kubectl exec -n <namespace> unstract-platform-redis-master-0 -c redis -- \
redis-cli REPLICAOF NO ONE
warning

This restores writability, not data. Anything already replaced during the takeover is gone: users will be signed out, caches repopulate from scratch, and keys belonging to the other application may remain until they expire or the database is flushed. Restart the Unstract services afterwards so they drop any pooled connections.

Scope

This restricts inbound connections to Redis. It does not restrict outbound connections from the Unstract namespace, and it does not protect other applications' datastores — each deployment needs its own equivalent policy, including LLMWhisperer if you run it on-prem.