skip to content
Sebastien Maintrot

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.

  1. Configure the Application to Use a Non-Privileged Port: Modify your application to listen on a port above 1024, such as 8080.

  2. 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:

    service.yaml
    apiVersion: v1
    kind: Service
    metadata:
    name: my-service
    spec:
    selector:
    app: my-app
    ports:
    - protocol: TCP
    port: 80 # External port
    targetPort: 8080 # Container port

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:

pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-container
image: my-app-image
ports:
- containerPort: 80
securityContext:
capabilities:
add: ["NET_BIND_SERVICE"]
runAsNonRoot: true

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:

  1. Install libcap2-bin and Set the Capability: Update your Dockerfile to install the necessary tool and grant the permission:

    DOCKERFILE
    FROM ubuntu:latest
    RUN apt-get update && apt-get install -y libcap2-bin
    RUN setcap 'cap_net_bind_service=+ep' /path/to/your-binary
    USER nonroot
    CMD ["/path/to/your-binary"]
  2. 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.