Linkerd’s authorization policies are surprisingly permissive by default, meaning you often have to explicitly deny access rather than grant it.
Let’s watch what happens when we try to access a frontend service from an unauthorized backend pod without any policies in place.
# On the backend pod
curl http://frontend.default.svc.cluster.local:8080
And here’s the output you’ll see on the frontend service’s logs:
2023-10-27T10:30:00Z ERROR http: server: invalid request: received request from unauthenticated client
This "unauthenticated client" message is key. Linkerd, by default, trusts everyone within the cluster. It’s only when you introduce authorization policies that it starts checking identities.
Here’s a typical scenario: you have a frontend service that should only be accessible by your api service, and nothing else. Without policies, any pod in the cluster can talk to frontend.
Let’s set up the policy to restrict access. We’ll create a NetworkAuthentication policy that defines who is allowed to connect.
apiVersion: policy.linkerd.io/v1alpha1
kind: NetworkAuthentication
metadata:
name: allow-api-to-frontend
namespace: default
spec:
networks:
- subnet: 10.1.0.0/16 # Example subnet for your cluster's pods
ports:
- 8080 # The port your frontend service listens on
peers:
- service: api.default.svc.cluster.local
This policy says: "On the default namespace, for any pod connecting to port 8080, if the connecting pod’s identity is api.default.svc.cluster.local, allow the connection."
Now, if we try to curl frontend from the backend pod again:
# On the backend pod
curl http://frontend.default.svc.cluster.local:8080
You’ll get a timeout. The frontend service won’t even see the request because Linkerd’s data plane intercepts it, checks the policy, and drops it before it reaches the application.
The NetworkAuthentication policy works by inspecting the client’s identity, which is established via mTLS by Linkerd’s control plane. When a request arrives at the frontend service’s pod, the Linkerd proxy sidecar inspects the TLS certificate of the incoming connection. If the certificate’s identity (specifically, the spiffe.io URI within the SAN) matches an entry in the peers list of a matching NetworkAuthentication policy for that port, the connection is allowed. Otherwise, it’s dropped.
This is powerful, but it means you need to be deliberate. If you have multiple services that need to talk to frontend, you’ll need to add them as peers to this policy, or create separate policies. For example, to also allow a web service:
apiVersion: policy.linkerd.io/v1alpha1
kind: NetworkAuthentication
metadata:
name: allow-api-and-web-to-frontend
namespace: default
spec:
networks:
- subnet: 10.1.0.0/16
ports:
- 8080
peers:
- service: api.default.svc.cluster.local
- service: web.default.svc.cluster.local
What if you want to allow any service in the default namespace to talk to frontend, but block external access? You can use the networks field to specify CIDRs.
apiVersion: policy.linkerd.io/v1alpha1
kind: NetworkAuthentication
metadata:
name: allow-cluster-to-frontend
namespace: default
spec:
networks:
- subnet: 10.1.0.0/16 # Your cluster's pod CIDR
ports:
- 8080
This policy allows any connection originating from within the 10.1.0.0/16 subnet to reach port 8080 of services in the default namespace. If your cluster uses a different pod CIDR, you’ll need to adjust this. The peers field is implicitly satisfied by the networks field here; if a connection is from a permitted network, it’s allowed.
The most common pitfall is forgetting to specify the correct namespace for your policies. A policy created in the linkerd namespace won’t apply to services in the default namespace. Policies are namespace-scoped.
Another common issue is using the wrong subnet in the networks field. If this doesn’t cover the actual IP addresses of your pods, traffic will be blocked. You can find your cluster’s pod CIDR by inspecting your Kubernetes network plugin configuration or checking the output of kubectl get nodes -o wide.
You might also be tempted to use ipBlocks instead of subnet for more granular control, but NetworkAuthentication policies are designed to work with subnet for CIDR-based network ranges. For IP-specific rules, you’d typically rely on the peers.service field to match by workload identity.
Finally, remember that policies are enforced by the Linkerd proxy. If the proxy isn’t running on your pod (i.e., the pod doesn’t have the linkerd.io/inject: enabled annotation), policies will have no effect on that pod.
After successfully restricting access, you’ll likely encounter a new error: resource exhausted: too many open files on your application pods if they aren’t configured to handle a high number of concurrent connections.