Expose Privileged Ports w/ Non-Root Containers on Kubernetes
/ 3 min read
Running containers as non-root users in Kubernetes is a recommended security practice, but it introduces challenges when you need to expose a privileged port, such as port 80 or 443. These ports, which fall in the 0-1023 range, are reserved for root users on most systems. In this article, we’ll explore two approaches for allowing non-root containers to expose privileged ports.
Why Non-Root Containers Cannot Expose Privileged Ports
In Linux, only processes with root privileges can bind to ports below 1024. This restriction helps ensure that critical services are not exposed unintentionally or compromised. When running your container as a non-root user, this restriction applies, and you cannot bind directly to these ports.
Workarounds for Exposing Privileged Ports from Non-Root Containers
Here are two approaches you can use to allow non-root containers to bind to privileged ports in Kubernetes, along with a third method using setcap
for better flexibility.
1. Run the Application on a Non-Privileged Port and Map It Using Kubernetes Service
The easiest way to circumvent the privileged port restriction is to configure your application to run on a non-privileged port, such as 8080, and then use Kubernetes to expose it on a privileged port like 80.
-
Configure the Application to Use a Non-Privileged Port: Modify your application to listen on a port above 1024, such as 8080.
-
Map the Port in a Kubernetes Service: Configure your Kubernetes Service to expose port 80 externally and route traffic to the non-privileged port on the container.
Here’s how the Service configuration would look:
This method allows you to adhere to non-root best practices while still making the application accessible over a standard HTTP port.
2. Use the CAP_NET_BIND_SERVICE
Capability
When it’s not feasible to run the application on a different port, you can grant the non-root user permission to bind to privileged ports using the CAP_NET_BIND_SERVICE
capability.
To add this capability in Kubernetes, use the securityContext
configuration in your Pod spec:
This configuration adds only the NET_BIND_SERVICE
capability, allowing your container to bind to privileged ports while still running as a non-root user.
3. Use setcap
to Grant Privileges on Specific Binaries
If you want a more targeted approach that grants permission only to a specific executable, you can use the setcap
command to set capabilities directly on the application binary. This way, only that binary is allowed to bind to privileged ports, maintaining tighter control.
Here’s how you can add setcap
in your Dockerfile to grant NET_BIND_SERVICE
:
-
Install
libcap2-bin
and Set the Capability: Update your Dockerfile to install the necessary tool and grant the permission: -
Deploy the Container: With this setup, the binary
/path/to/your-binary
can now bind to privileged ports, even though the container itself is running as a non-root user.
Using setcap
offers more granularity and restricts privileged port access solely to the binary, reducing the risk of misuse by other processes within the container.
⚠️ Warning ⚠️
Setcap will not work if you use securityContext
or Security Context Constraints (SCC)
(on Openshift) to Drop All Capabilities
Conclusion
Running applications as non-root users is crucial for security in Kubernetes, but it can complicate the process of exposing privileged ports. Using a non-privileged port with Kubernetes Service mapping, the CAP_NET_BIND_SERVICE
capability, or setting capabilities on specific binaries with setcap
, you can achieve the desired configuration while maintaining security best practices.