NetworkPolicy port rules bypassed when Linkerd proxy is injected
@alpeb is already working on this.
Since May 15, 2026.
- Dominant language
- Go
- Stars
- 11.5k
- Forks
- 1.4k
- Avg merge
- 3d 22h
- Merged PRs (30d)
- 53
Description
### What is the issue?
Hi team,
We've run into surprising behavior with Linkerd and Kubernetes NetworkPolicies and would appreciate some guidance.
What we observed
In our namespace we have a strict default-deny NetworkPolicy setup with explicit allow rules per port. Without Linkerd, this works exactly as expected — only the explicitly allowed ports are reachable between pods.
After enabling the Linkerd sidecar, pod-to-pod communication broke entirely. We traced this to the proxy needing inbound **and** outbound on port `4143` between meshed pods (the [docs ](https://linkerd.io/2.19/common-errors/network-policy/ )only mention inbound). Once we allowed `4143` in both directions, communication worked again — but then we noticed that all ports of a pod became reachable, not just the ones our NetworkPolicies explicitly allow.
It seems that because the proxy tunnels everything through 4143, the NetworkPolicy port rules on application ports (e.g. 8080, 8081) effectively no longer apply between meshed pods.
### How can it be reproduced?
```yaml
# pod1 exposes two ports: 8080 (http) and 8081 (management)
apiVersion: v1
kind: Pod
metadata:
name: pod1
labels:
app: pod1
annotations:
linkerd.io/inject: enabled
spec:
containers:
- name: app
image: my-app:latest
ports:
- containerPort: 8080
- containerPort: 8081
---
# pod2 should only be allowed to reach pod1 on 8080
apiVersion: v1
kind: Pod
metadata:
name: pod2
labels:
app: pod2
annotations:
linkerd.io/inject: enabled
spec:
containers:
- name: client
image: curlimages/curl
command: ["sleep", "infinity"]
---
# Default deny
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
# Allow only port 8080 from pod2 to pod1
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-pod2-to-pod1-http
spec:
podSelector:
matchLabels:
app: pod1
ingress:
- from:
- podSelector:
matchLabels:
app: pod2
ports:
- port: 8080
---
# Required so the proxies can talk at all
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-linkerd-proxy
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
ports:
- port: 4143
egress:
- to:
- podSelector: {}
ports:
- port: 4143
---
# Allow proxy -> control-plane
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-linkerd-control-plane
spec:
podSelector: { }
policyTypes:
- Egress
- Ingress
ingress: [ ]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: linkerd
```
Expected: pod2 can reaches pod1:8080, but not pod1:8081.
Actual: pod2 can reaches both pod1:8080 and pod1:8081.
### Logs, error output, etc
-
### output of `linkerd check -o short`
```text
linkerd-identity
----------------
? issuer cert is valid for at least 60 days
issuer certificate will expire on 2026-05-09T19:08:11Z
see https://linkerd.io/2/checks/#l5d-identity-issuer-cert-not-expiring-soon for hints
linkerd-webhooks-and-apisvc-tls
-------------------------------
? proxy-injector cert is valid for at least 60 days
certificate will expire on 2026-05-08T19:08:14Z
see https://linkerd.io/2/checks/#l5d-proxy-injector-webhook-cert-not-expiring-soon for hints
? sp-validator cert is valid for at least 60 days
certificate will expire on 2026-05-08T19:08:11Z
see https://linkerd.io/2/checks/#l5d-sp-validator-webhook-cert-not-expiring-soon for hints
? policy-validator cert is valid for at least 60 days
certificate will expire on 2026-05-08T19:08:11Z
see https://linkerd.io/2/checks/#l5d-policy-validator-webhook-cert-not-expiring-soon for hints
linkerd-version
---------------
? cli is up-to-date
is running version 26.4.4 but the latest edge version is 26.5.1
see https://linkerd.io/2/checks/#l5d-version-cli for hints
control-plane-version
---------------------
? control plane is up-to-date
unsupported version channel: enterprise-2.18.8
see https://linkerd.io/2/checks/#l5d-version-control for hints
? control plane and cli versions match
control plane running enterprise-2.18.8 but cli running edge-26.4.4
see https://linkerd.io/2/checks/#l5d-version-control for hints
linkerd-control-plane-proxy
---------------------------
? control plane proxies are up-to-date
some proxies are not running the current version:
* linkerd-destination-c7677c45f-b95fz (enterprise-2.18.8)
* linkerd-destination-c7677c45f-cc2gg (enterprise-2.18.8)
* linkerd-destination-c7677c45f-mfsms (enterprise-2.18.8)
* linkerd-enterprise-6c5dbb896c-fpqh2 (enterprise-2.18.8)
* linkerd-enterprise-6c5dbb896c-h2xvt (enterprise-2.18.8)
* linkerd-enterprise-6c5dbb896c-jvnjn (enterprise-2.18.8)
* linkerd-identity-649d5d5557-nfmkz (enterprise-2.18.8)
* linkerd-identity-649d5d5557-sdj9s (enterprise-2.18.8)
* linkerd-identity-649d5d5557-sdxdn (enterprise-2.18.8)
* linkerd-proxy-injector-776564db67-l7nxr (enterprise-2.18.8)
* linkerd-proxy-injector-776564db67-qg5kk (enterprise-2.18.8)
* linkerd-proxy-injector-776564db67-xgkfp (enterprise-2.18.8)
see https://linkerd.io/2/checks/#l5d-cp-proxy-version for hints
? control plane proxies and cli versions match
linkerd-destination-c7677c45f-b95fz running enterprise-2.18.8 but cli running edge-26.4.4
see https://linkerd.io/2/checks/#l5d-cp-proxy-cli-version for hints
linkerd-viz
-----------
? viz extension proxies are up-to-date
some proxies are not running the current version:
* metrics-api-d6dc69f98-lvfzr (enterprise-2.18.3)
* prometheus-6bb4f9c687-gxktq (enterprise-2.18.3)
* tap-85b7d5bd5-dqpb4 (enterprise-2.18.3)
* tap-injector-595b69f9f7-qrm5q (enterprise-2.18.3)
* web-6c894f57fd-5pqxv (enterprise-2.18.3)
see https://linkerd.io/2/checks/#l5d-viz-proxy-cp-version for hints
? viz extension proxies and cli versions match
metrics-api-d6dc69f98-lvfzr running enterprise-2.18.3 but cli running edge-26.4.4
see https://linkerd.io/2/checks/#l5d-viz-proxy-cli-version for hints
Status check results are v
```
### Environment
- Kubernetes 1.33
### Possible solution
_No response_
### Additional context
Question
We understand that mTLS tunnels traffic through `4143`, so the CNI only sees pod2 → pod1:4143 and can't distinguish the original target port. That makes sense in theory — but it does mean our NetworkPolicy port rules silently lose effect once a workload joins the mesh, which is unexpected for users coming from a NetworkPolicy setup.
Is there a recommended way to keep port-level enforcement for meshed pods using NetworkPolicies? Otherwise it would be very helpful to have this called out prominently in the NetworkPolicy/Linkerd integration docs — we only figured it out after a lot of debugging.
Thanks a lot for any insight!
### Would you like to work on fixing this bug?
None
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.