Kubernetes DNS and Network Policies: Secure Service Communication in Your Cluster
Kubernetes, the go-to platform for orchestrating containerized applications, simplifies pod and service communication through an essential component: DNS. However, unrestricted communication between services in a cluster can expose vulnerabilities. This is where Network Policies come into play, giving you fine-grained control over how services interact within the cluster. In this article, we’ll walk through how Kubernetes DNS works, how you can use it to enable service-to-service communication, and how Network Policies enforce security, preventing unauthorized access.
Kubernetes DNS: Simplifying Service Discovery
DNS (Domain Name System) plays a crucial role in Kubernetes by providing name resolution within the cluster. It’s the key to allowing services and pods to communicate with each other seamlessly, using human-readable names instead of hardcoded IP addresses.
CoreDNS: The Heart of Kubernetes DNS
Kubernetes uses CoreDNS as the default DNS server. CoreDNS handles DNS resolution for both services and pods within the cluster. For example, if you create a service named backend
, Kubernetes automatically generates a DNS entry for it in the form of:
backend.default.svc.cluster.local
Here’s the breakdown:
backend
: The name of the service.default
: The namespace where the service is running.svc.cluster.local
: A domain suffix applied to all services in the cluster.
When a pod needs to interact with the backend
service, it can simply use the DNS name backend.default.svc.cluster.local
, and CoreDNS will resolve this name to the internal ClusterIP of the backend
service.
Example: Frontend and Backend Pod Interaction via DNS
Let’s create a simple example to demonstrate how a frontend pod communicates with a backend service using Kubernetes DNS.
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: nginx
We’ll expose the backend deployment as a service:
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 80
Next, we create a frontend
pod that interacts with the backend
service:
# frontend-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: frontend
spec:
containers:
- name: frontend
image: curlimages/curl
command: ["sleep", "3600"]
Now, let’s test DNS resolution by using nslookup
and curl
from the frontend pod:
Check DNS Resolution:
kubectl exec -it frontend -- nslookup backend.default.svc.cluster.local
The DNS query will return the ClusterIP of the backend service, which allows the frontend pod to communicate with it.
Make a Request from Frontend to Backend:
kubectl exec -it frontend -- curl backend.default.svc.cluster.local
The frontend
pod can now send HTTP requests to the backend
service using the DNS name, without needing to know its internal IP address.
Kubernetes Network Policies: Controlling Pod Communication
While Kubernetes DNS enables seamless service discovery, unrestricted communication between pods may not always be desirable. Network Policies allow you to enforce fine-grained control over pod-to-pod communication, acting as a firewall to ensure only permitted traffic is allowed.
By default, all pods in Kubernetes can communicate with each other, but Network Policies let you define rules that:
- Restrict pod-to-pod communication.
- Allow or deny ingress (incoming) and egress (outgoing) traffic.
- Secure communication between services.
Basic Components of a Network Policy
- Pod Selector: Specifies the target pods the policy applies to.
- Policy Types: Defines whether the policy applies to ingress (incoming) traffic, egress (outgoing), or both.
- Ingress/Egress Rules: Specifies the allowed or denied traffic for the selected pods.
Example 1: Deny All Traffic
Let’s start with a very restrictive policy that blocks all ingress and egress traffic to a set of pods.
# deny-all-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
This policy denies all incoming and outgoing traffic for all pods in the default
namespace. No pod can talk to any other pod or external resource.
Example 2: Allow Frontend to Backend Communication
In a more realistic scenario, we’ll create a network policy that:
- Allows the
frontend
pod to communicate with thebackend
service. - Ensures that DNS traffic to CoreDNS is allowed for proper name resolution.
# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
egress:
- to:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Policy Breakdown:
- Pod Selector: The policy applies to pods labeled
app=backend
. - Ingress Rule: Only allows traffic from pods labeled
app=frontend
to reach thebackend
service. - Egress Rule: Allows traffic from the
backend
pod to bothfrontend
and CoreDNS (for DNS resolution). - DNS Rules: Ports 53 (TCP/UDP) are opened for communication with the DNS service.
Testing the Network Policy
Apply the Network Policy:
kubectl apply -f network-policy.yaml
Test Communication from Frontend Pod: The frontend pod should still be able to access the backend service via DNS and send HTTP requests:
kubectl exec -it frontend -- curl backend.default.svc.cluster.local
Testing from Unallowed Pod: If you create another pod (say otherpod
) that doesn't match the allowed labels, it will not be able to communicate with the backend
service.
Advanced Network Policies
Here are a few more use cases where Network Policies shine:
1. Restricting Egress Traffic:
You can control which external IPs or services a pod can access. For example, allow access to only a specific API or DNS server.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 8.8.8.8/32 # Google DNS
ports:
- protocol: UDP
port: 53
- ipBlock:
cidr: 203.0.113.0/24 # External API range
2. Isolating Namespaces:
Use Network Policies to ensure that pods in one namespace can’t communicate with pods in another namespace unless explicitly allowed.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-cross-namespace
namespace: app-namespace
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: app-namespace
Conclusion: DNS + Network Policies = Secure, Efficient Communication
In Kubernetes, DNS enables seamless service discovery, allowing pods to communicate using simple names instead of complex IP addresses. Meanwhile, Network Policies add a vital layer of security by controlling how services communicate, both within the cluster and with external resources.
By mastering both Kubernetes DNS and Network Policies, you can ensure your microservices architecture is scalable, secure, and robust — allowing only authorized communication and protecting your applications from unwanted traffic.