adllm Insights logo adllm Insights logo

Resolving EACCES when a Docker container tries to bind to a low port without CAP_NET_BIND_SERVICE on a SELinux-enabled host

Published on by The adllm Team. Last modified: . Tags: docker selinux linux-capabilities container-security devops

Introduction

When deploying applications in Docker containers, binding to low-numbered ports (below 1024) is often necessary, especially for services like HTTP (port 80). However, this can result in an EACCES error on systems where SELinux is enabled, particularly if the CAP_NET_BIND_SERVICE capability isn’t granted to the container. This article explores the root causes of this issue and provides solutions to enable containers to bind to low ports securely and efficiently.

Understanding the Core Problem

Docker and SELinux

Docker uses OS-level virtualization to run applications in isolated containers. By default, these containers lack the necessary Linux capabilities to bind to privileged ports (below 1024), unless explicitly granted. On a SELinux enabled system, additional security policies further restrict these operations, necessitating a careful configuration of both Docker and SELinux to avoid EACCES errors.

The Role of CAP_NET_BIND_SERVICE

The CAP_NET_BIND_SERVICE is a Linux capability that allows processes to bind to ports below 1024 without requiring root privileges. Without this capability, attempts to bind to such ports will fail with an EACCES error. Granting this capability is essential for services running in containers that need to operate on standard service ports.

Best Practices for Resolving EACCES

Granting CAP_NET_BIND_SERVICE to Docker Containers

To allow a Docker container to bind to low-numbered ports, you can grant it the CAP_NET_BIND_SERVICE capability using Docker’s --cap-add flag. This is accomplished with the following command:

1
docker run --cap-add=NET_BIND_SERVICE -p 80:80 myapp

This command ensures that the container has the capability to bind to port 80, facilitating the operation of web services without encountering an EACCES error.

Configuring SELinux for Docker

SELinux policies need to be adjusted to allow containers to bind to low ports. This can be done using the semanage command:

1
semanage port -a -t http_port_t -p tcp 80

This command adds a new port context for HTTP services, allowing Docker containers to bind to port 80 while SELinux is in enforcing mode.

Detailed Example: Running a Web Server on Port 80

Let’s walk through configuring a Docker container to run a simple web server on port 80 with SELinux enforcing.

Step 1: Create a Simple Web Server Dockerfile

First, create a Dockerfile with a basic web server setup:

1
2
FROM nginx:latest
EXPOSE 80

This Dockerfile uses the latest Nginx image and exposes port 80 for HTTP traffic.

Step 2: Build and Run the Docker Container

Build the Docker image and run the container with the necessary capabilities:

1
2
docker build -t my-nginx .
docker run --cap-add=NET_BIND_SERVICE -p 80:80 my-nginx

This sequence builds the image and runs the container, allowing it to bind to port 80.

Challenges and Diagnostic Techniques

Common Pitfalls

  • Over-permissive Policies: Avoid granting unnecessary capabilities or disabling SELinux, which can lead to security risks.
  • Misconfigured SELinux Contexts: Ensure SELinux contexts are correctly configured to prevent service startup failures.

Diagnostic Tools

  • SELinux Status: Verify SELinux mode using getenforce and sestatus.
1
2
sestatus
getenforce
  • Audit Logs: Check /var/log/audit/audit.log for SELinux denials related to Docker.
1
ausearch -m avc -ts recent
  • Docker Logs: Inspect Docker logs for error messages.
1
docker logs <container_id>

Conclusion and Future Considerations

By understanding and configuring both Docker and SELinux correctly, developers can resolve EACCES errors related to binding low ports. Future advancements in container security and automation tools like Ansible can further streamline these configurations, ensuring secure and efficient deployments.