Skip to content

Latest commit

 

History

History
149 lines (109 loc) · 11 KB

README.md

File metadata and controls

149 lines (109 loc) · 11 KB

mjmccans/docker-socket-proxy

This is a reverse proxy for your Docker socket that allows you to control what Docker API endpoints can be accessed by Docker clients such as Portainer, Diun and Watchtower.

Using a proxy is important from a security perspective because giving direct access to your Docker socket essentially means giving root access to your host, and also could allow undesired changes to your other Docker containers and Docker environment.

Inner Workings

This Docker image is based upon the official Nginx Alpine image with a small Python script that creates a nginx.conf file based upon environment labels you set when you run the image. By default certain read-only API commands are allowed (see below for more details), but you can adjust to suit your needs. Where access to a certain API command has been revoked, an HTTP 403 Forbidden status is returned and there is an option that allows you to include additional details in the message (see below for more details).

Security Warnings and Recommendations

  • Never expose this container's port to a public network as this would allow anyone to connect to the port and issue allowed docker commands. This container is intended to only be exposed on the internal Docker network of the service that will use the proxy.
  • Only allow access to those API commands the service requires. One way to do this is to start with the default configuration, watch the logs (see below for more information), and then only add those API commands that are necessary for your use case.

Usage

  1. Run the container using a command similar to the following (Note: You may need the --privileged flag due to SELinux/AppArmor, but try it without first):

    $ docker container run \
        -d --privileged \
        --name docker-proxy \
        -e CONTAINERS=1 \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -p 127.0.0.1:2375:2375 \
        mjmccans/docker-socket-proxy
    
  2. You can list running containers because we set the CONTAINERS=1 environment variable:

    $ docker -H localhost:2375 container ls
      CONTAINER ID   IMAGE                          COMMAND                  CREATED         STATUS         PORTS                              NAMES
      4f828bbd0dcc   mjmccans/docker-socket-proxy   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp, 127.0.0.1:2375->2375/tcp   docker-proxy
    
  3. However, you cannot list images because we did not override the default IMAGES=0 environment label:

    $ docker -H localhost:2375 image ls
    Error response from daemon: 403 Forbidden: docker-socket-proxy is configured to not allow access to this function. To enable this function turn on the IMAGES option.
    

See the "examples" folder for some docker compose examples.

Logs / Return Messages

The container provides useful log output showing what API calls have been made, the return codes and additional details that can be helpful for configuration and debugging. You can turn on the DESCRIPTIVE_ERRORS option to get more descriptive logging and client-side messages where a request is rejected. Turning on the DESCRIPTIVE_ERRORS option creates a more complex nginx.conf file that contains more 'locations', so I recommend turning the option off once you have everything set up.

Example

In the example below, we can see that the first call to info has been accepted (return code 200) and the call to volumes\create has been rejected (return code 403). In addition, the last part of each line shows what environment variable option was in play to create this outcome. Note that if you do not have the DESCRIPTIVE_ERRORS option enabled you will not be given a specific environment variable option where an API call is rejected.

[25/Jan/2023:15:25:12 -0500] "GET /info HTTP/1.1" 200 [Location: INFO]
[25/Jan/2023:15:25:16 -0500] "POST /volumes/create HTTP/1.1" 403 [Location: VOLUMES_CREATE (BLOCKED)]

On the client side you can also get helpful information. With the DESCRIPTIVE_ERRORS option enabled you would get the following return message on the client side:

403 Forbidden: docker-socket-proxy is configured to not allow access to this function. To enable this function turn on the VOLUMES_CREATE option.

Without the DESCRIPTIVE_ERRORS option enabled you would get a more generic return message on the client like the following:

403 Forbidden: docker-socket-proxy is configured to not allow access to this function.

Options

Granting or revoking access to certain Docker API calls is done by setting environment variables, and the environment variables generally match the Docker API command they relate to.

To grant or revoke access set the corresponding environment variable as follows:

  • 0 to revoke access.
  • 1 to grant access.

Below is a list of all of the options and each has a link to the Docker API Specification document so you can understand what each does.

Granted by Default

By default, the following options are set (and you can more details on each below):

  • DESCRIPTIVE_ERRORS
  • EVENTS
  • PING
  • VERSION

All Options

Logging and Output

  • DESCRIPTIVE_ERRORS: See above for more details.

GET Commands (Read-Only)

  • CONFIGS
  • CONTAINERS: Allows access to all GET commands for containers (e.g., list, inspect, logs, etc.)
  • EVENTS
  • IMAGES: Allows access to all GET commands for images (e.g., list, inspect, history, etc.)
  • INFO
  • NETWORKS: Allows access to all GET commands for networks (e.g., list, inspect, etc.)
  • PING
  • PLUGINS: Allows access to all GET commands for plugins (e.g., list, get privileges, inspect, etc.)
  • SECRETS: Allows access to all GET commands for secrets (e.g., list, inspect, etc.)
  • SERVICES: Allows access to all GET commands for services (e.g., list, inspect, logs, etc.)
  • SWARM: Allows access to all GET commands for swarm (e.g., inspect, unlock key, etc.)
  • SYSTEM
  • TASKS: Allows access to all GET commands for tasks (e.g., list, inspect, etc.)
  • VERSION
  • VOLUMES: Allows access to all GET commands for volumes (e.g., list, inspect, etc.)

POST Commands

Delete Commands

Inspiration

This project was inspired by tecnativa/docker-socket-proxy and fluencelabs/docker-socket-proxy. Both of those projects build upon haproxy while I am more familiar (but by no means an expert) with Nginx so I have used Nginx for this project. I also wanted to include a finer level of control similar to what is included in fluencelabs' project, and also wanted to include more detailed log messages and client messages to assist with setup and debuging.