Portless Ports: Demystifying Kubernetes Port Forwarding
Introduction
Recently, I was exploring Hexmos' infrastructure, trying to learn all the steps involved in loading up the Feedback app in the browser. During my investigation of a packet's path to the server and analysis of the Nginx configuration file that governs how the web server handles incoming requests, I discovered that the requests were being redirected to http://localhost:32264/. So, I did a
curl http://localhost:32264
netstat -tuln | grep 32264
Why Kubernetes Service Ports Aren't Listed with Others
When I conducted further research, I discovered that the ports of services running within a container are not displayed in netstat, and the karmaweb service (feedbackweb) was operating in a Kubernetes environment. In Kubernetes, service ports are managed differently from host ports. When a service is exposed Kubernetes will assign a port number to it which is referred to as a "service port". These ports won’t be listed with other ports because they are hidden by the Kubernetes network layer. To get more context we can use kubectl is a command line to interact with Kubernetes. The following command will list out the karmaweb service
kubectl get rc, services | grep karmaweb
Kubernetes Basics
Overview
Kubernetes is an open-source orchestration tool developed by Google for managing microservices or containerized applications across a distributed cluster of nodes. Kubernetes provides a highly resilient infrastructure with zero downtime deployment capabilities, automatic rollback, scaling, and self-healing of containers (which consists of auto-placement, auto-restart, auto-replication, and scaling of containers based on CPU usage).
Reason for using Kubernetes
- Scalability: It is easy to scale the applications using Kubernetes. It can automatically adjust the number of containers to handle increased traffic or reduce resources during periods of lower demand.
- High Availability: Kubernetes ensures high availability by distributing containers across multiple nodes in a cluster. Kubernetes can reschedule containers to healthy nodes if a node or container fails to maintain application availability.
- DevOps and CI/CD Integrations: Kubernetes fits seamlessly into DevOps and CI/CD pipelines, allowing for automated testing, deployment, and scaling of applications.
Users and Uses Cases
- Enterprises.
- Startups.
- DevOps Teams.
- System Administrator.
- Internet of Things.
About iptables
Iptables and ip6tables are used to set up, maintain, and inspect the tables of IPv4 and IPv6 packet filter rules in the Linux kernel. These tables contain sets of rules, called chains, that will filter incoming and outgoing data packets. These chains are stored in tables.
Tables and Chains
-
Filter: Used for packet filtering and deciding whether to allow or block packets.
- INPUT Chain: for altering packets destined for local sockets on the same device or the server
- OUTPUT Chain: It deals with outgoing packets generated by processes running on the firewall itself.
- Forward Chain: handles packets that are routed through the firewall, neither destined for nor generated by the firewall itself.
-
NAT: Used for Network Address Translation, such as port forwarding and address translation.
- PREROUTING Chain: This chain is used for altering packets as they arrive at the firewall, typically before routing decisions are made. It's often used for destination NAT (DNAT) or port forwarding.
- POSTROUTING Chain: It processes packets after routing decisions have been made but before they leave the firewall.
- OUTPUT Chain: Similar to the Filter Table, but used for NAT-related decisions
-
Mangle: Used for advanced packet-level manipulation and QoS (Quality of Service) settings.
- PREROUTING Chain: Similar to the NAT Table. But allows more advanced packet manipulation
- INPUT Chain: Similar to the Filter Table, allowing for more detailed packet alterations.
- FORWARD Chain: Similar to the Filter Table, is used for packets that are routed through the firewall.
- OUTPUT Chain: Similar to the Filter Table, for processing outgoing packets.
Analysis of Kubernetes iptables rules
Closer look
Now let's take a closer look at the chains and rules used for Kubernetes. For this procedure we are using iptables as mentioned above this tool will be useful for this.
iptables -L -t nat | grep -i karmaweb:http
- -L, to list the rules of the specified tables.
- -t nat, to specify the table from which the rules are to be displayed.
From the PREROUTING and OUTPUT chains we can see that all the packets incoming and outgoing enter KUBE-SERVICES, set of automatically generated iptables rules that are used to handle service traffic within the cluster. Let's first look at the KUBE-SERVICES chain which as it is the entry point for service packets, matches the destination IP: port and dispatches the packet to the corresponding KUBE-SVC-* chain.
sudo iptables -L KUBE-SERVICES -t nat | grep karmaweb:http
sudo iptables -L KUBE-SVC-5XYKYCRLOJZB2ITL -t nat
-
First rule
- target : It specifies the next checkpoint of the packet which is KUBE-MARK-MASQ, if it satisfies the rules mentioned here.
- port : This part checks the protocol used by packet. Here the protocol mentioned is tcp.
- opt: It's the option or flags, it provides how the rule is to be applied for the packet. Here no options are mentioned.
- source : This will check the source of the packet, the packets not coming from ip-10-224-0-0.ap-south-1.compute.internal/16 domain will be accepted, this domain translates to the IP range 10.244.0.0 to 10.244.255.255.
- destination: Specify the destination of the packet., the domain is ip-10-110-199-117.ap-south-1.compute.internal translates to IP 10.110.199.177 If the packets pass the above rule, it will undergo an alteration process where the source address of the packet will be changed. This takes place in KUBE-POSTROUTING chain.
sudo iptables -L KUBE-POSTROUTING -t nat
-
Second Rule
- target: The target mentioned here is KUBE-SEP-RX5EM3L6A5YPWQT5, which is another chain.
- prot: No protocol is specified here.
- opt: It is empty as above rulesource: No source is specified.
- destination : No destination is specified.
KUBE-SVC-* acts as a load balancer and distributes packets to KUBE-SEP-* chains. The number of KUBE-SEP-* chains is equal to the number of endpoints behind the service (i.e. number of running pods). For now, we only have one endpoint. Now the request will be dispatched to KUBE-SEP-RX5EM3L6A5YPWQT5 Next we have to inspect KUBE-SEP-RX5EM3L6A5YPWQT5 chain.
sudo iptables -L KUBE-SEP-RX5EM3L6A5YPWQT5 -t nat
-
First rule
- target : It specifies the next checkpoint of the packet which is KUBE-MARK-MASQ.
- port : This part checks the protocol used by packet. Here the protocol mentioned is tcp.
- opt: It's the option or flags, it provides how the rule is to be applied for the packet. Here no options are mentioned.
- source : No source is specified, It can be from anywhere.
- destination: Specify the destination of the packet, the domain is ip-10-244-5-241.ap-south-1.compute.internal translates to IP 10.244.5.241.
- /* default/karmaweb:http */ : This is a comment that says this rule is related to karamweb service.
-
Second rule
- This redirects all the packets to the podIP which is 10.244.5.241:3000 This rule only care about protocol which has to be tcp, source and destination can be anything.
- This redirects all the packets to the podIP which is 10.244.5.241:3000 This rule only care about protocol which has to be tcp, source and destination can be anything.
Port Forwarding
The above scenario is an example of port forwarding in Kubernetes. Port forwarding is a concept in networking that enables traffic from the internet to reach a device within a private network. It can be done by router or firewall, so the service hosted in local devices can be accessed from the web.
Experiment on port forwarding
Now we will try to recreate the above scenario using iptables to demonstrate port forwarding. Our goal is to redirect the request done on localhost:8909 to 15.0.0.1:8764. To demonstrate that we are setting up a Python script that will listen on 15.0.0.1:8764. This Python script will be running in a docker container.
import socket def fake_service(ip, port): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((ip, port)) server_socket.listen(1) print(f'Fake service is listening on port {port}') while True: client_socket, client_addr = server_socket.accept() print(f'Connection from: {client_addr}') http_response = b'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nFake service response\n' client_socket.send(http_response) client_socket.close() if __name__ == '__main__': ip = "localhost" port_number = 8765 fake_service(ip,port_number)
Find source code here Doing
curl http://localhost:8764
by accessing the terminal of the container .
curl http://localhost:8764
netstat -tuln
command is used to display active network connections and listening ports on a Linux system.
Creating a Dummy Network Interface
Relation between the network interface and IP
In order to connect to the network, a computer must have at least one network interface, Each network interface must have its own unique IP address. The IP address that you give to a host is assigned to its network interface. If you add a second network interface to a machine, it must have its own unique IP number. Adding a second network interface changes the function of a machine from a host to a router.
if __name__ == '__main__': ip = "15.0.0.1" port_number = 8764 fake_service(ip,port_number)
curl gives
curl http://15.0.0.1:8764
Setting Up iptables
Added an OUTPUT Chain in NAT tables in iptables.
sudo iptables -t nat -A OUTPUT -p tcp --dport 8909 -j DNAT --to-destination 15.0.0.1:8765
- -t nat: specify the table where the rule has to be added.
- -A OUTPUT: to append a rule to OUTPUT chain.
- -p tcp: specify the protocol has to be tcp.
- --dport 8909: to set the destination port.
- -j DNAT --to-destination 15.0.0.1:8765: This is the action part of the rule. This says the packet’s destination IP address and port have to be rewritten to 15.0.0.1:8765 if it meets the above conditions.
Successful port forwarding
Now when we do
curl http://localhost:8909
will give the same results as
curl http://15.0.0.1:8765
Conclusion
In this article, we explored the complexities of Kubernetes network management, iptables, and port forwarding. We investigate Hexmos infrastructure and discover how Kubernetes hides service ports from traditional tools like netstat. We delve into the fundamentals of Kubernetes, explore the intricacies of iptables, and dissect Kubernetes iptables rules. We demystify port forwarding and demonstrate its practical application. This comprehensive guide sheds light on Kubernetes networking for network administrators and developers in the constantly evolving world of container orchestration.
Hacker News Post