#cli #docker #firewall

bin+lib dfw

Docker firewall framework, in Rust

9 releases (5 stable)

1.2.1 Dec 13, 2020
1.2.0 Jul 13, 2020
1.1.0 May 26, 2020
1.0.1 Sep 28, 2019
0.0.1-placeholder Jun 1, 2017

#222 in Development tools

25 downloads per month

MIT/Apache

195KB
3.5K SLoC

DFW - Docker Firewall Framework in Rust

Breaking changes coming from v0.x to v1.x

Starting with version 1.0, DFW introduced the nftables backend and made it the default firewall-backend used. If you are upgrading DFW but don't want to switch to nftables, you can provide the --firewall-backend iptables parameter to DFW (this requires at least DFW v1.2).

Please note that no matter if you transition to nftables or not, v1.0 introduced breaking changes to the configuration. Please consult the migration documentation on how to update your configuration.


  1. Overview
    1. Example
  2. Getting started
  3. Configuration
  4. IPv6 support
    1. Example: webserver reachable via IPv6
  5. Supported Docker versions
  6. License
    1. Contribution

Overview

DFW is conceptually based on the Docker Firewall Framework, DFWFW. Its goal is to make firewall administration with Docker simpler, but also more extensive by trying to replace the Docker built-in firewall handling.

This is accomplished by a flexible configuration that defines how the firewall should be built up. While DFW is running, Docker container events will be monitored and the rules rebuilt when necessary.

One of the key-features of DFW (and DFWFW before it) is to not require the running containers to publish their ports on the host (à la docker container run --publish 80:8080), but rather use the network-address translation (NAT) features of the host-firewall to forward packets directly to the port in the container.1

DFW supports the following firewall backends:

  • iptables
  • nftables

You can choose the one that works best for you.2

1 This only applies if you use IPv4 on your host. If you want to have IPv6-support, you still need to publish the ports. See [IPv6 support](#configuration-ipv6) for more information.
2 Please make sure to not mix firewall-backends: if you are already using one on your host, do not use the other one with DFW.

Example

Assume that you want to run a reverse proxy in Docker that should proxy traffic to a web-application that is also running in Docker. With the regular tools provided by Docker you would simply host-bind the port, put the two Docker containers on the same Docker network and would have a working solution.

While this works quite well, having Docker handling the firewall rules has a few potential drawbacks:

  1. Traffic to host-mounted ports is not restricted by default.

    While this usually is the desired behaviour, it might hurt you if you just want to launch a service and test it locally.3

    With DFW you have to be explicit: if you want your service to be reachable, you have to configure exactly which container should be reachable from where. While this does incur an upfront cost in terms of effort, it can reward you afterwards by ensuring you don't accidentally expose a service you didn't intend to expose.

  2. Traffic between containers on the same Docker network is not restricted.

    Again: most of the time this is the desired behaviour. When it isn't though, Docker does not give you the tools to restrict this traffic.

    DFW allows you to configure exactly how you want containers to be able to communicate with each other, both in the same Docker network and across Docker networks.

    In the example above we want the reverse proxy to communicate with the web-application, but the web-application should not be able to initiate a connection to the reverse proxy. DFW allows you to implement this scenario.

If you have not encountered the drawbacks described above and are happy with the features provided by Docker, you might not need DFW. But if you have, or are simply interested in trying DFW out, take a look at the reverse proxy example which will work you through the proposed example.

3 You can of course bind the port to `127.0.0.1`, but you have to be explicit about that, which is easy to forget.

Getting started

If you are already a user of DFW and are looking to upgrade to a newer version, consult the matching migration documentation:

If you are starting fresh, the first step is to decide on a firewall backend:

  • nftables

    nftables can be seen as a newer generation of iptables, and it will replace iptables in most Linux distributions at some point. (It already is the default in e.g. Debian 10 Buster.)

    If you are starting fresh on a host where you have not used either backend yet, nftables is the suggested backend.

  • iptables

    While iptables is the older netfilter implementation, it is still a valid firewall-backend and still finds extensive use across many distributions.

    If you are already using iptables and have a configuration that you don't want to re-do, feel free to use the iptables backend with DFW.

Once you have decided which backend you want to use, please consult the backend-specific documentation on how to proceed further:

Configuration

The general configuration happens across six categories:

  • global_defaults

    This category defines global, default values to be used by DFW and the other categories.

  • backend_defaults

    This category defines configuration values that are specific to the firewall-backend used.

  • container_to_container

    This controls the communication between containers and across Docker networks.

  • container_to_wider_world

    This controls if and how containers may access the wider world, i.e. what they can communicate across the OUTPUT chain on the host.

  • container_to_host

    To restrict or allow access to the host, this section is used.

  • wider_world_to_container

    This controls how the wider world, i.e. whatever comes in through the INPUT chain on the host, can communicate with a container or a Docker network.

  • container_dnat

    This category allows you to define specific rules for destination network address translation, even or especially across Docker networks.

See the examples and configuration types for detailed descriptions and examples of every configuration section.

IPv6 support

If you make a container publicly available, DFW will use "destination NATting" and "masquerading" to redirect incoming packets to the correct internal IP of the container, and then correctly redirect the reponses back to the original requester. Every default installation of Docker does not assign private IPv6 addresses to networks and containers, it only assigns private IPv4s.

Generally there is also no need for private IPv6 addresses: Docker uses a proxy-binary when host-binding a container-port to perform the translation of traffic from the host to the container. This host-binding is compatible with both IPv4 and IPv6, which means internally a single IPv4 is sufficient.

As mentioned, DFW does work differently: since it uses NAT to manage traffic, it effectively would have to translate incoming packets from IPv6 to IPv4 and the responses from IPv4 to IPv6, something that is not supported by nftables and iptables.

The consequence of this is that if you want your services to be reachable via IPv6, you have to ensure the following things:

  1. You have to publish the ports of the containers you want to be able to reach on your host through the Docker-integrated run-option --publish.

    The host-port you select here is the one under which it will be reachable publicly later, i.e. if you want your webserver to be reachable from host-ports 80 and 443, you need to publish the container ports under 80 and 443.

  2. In your wider-world-to-container rule, the host-port part of your exposed port must match the port you published the container ports under (although it doesn't have to match the container-port itself).

    As part of the wider-world-to-container rule DFW will create the firewall-rules necessary for the host-bound ports to be reachable via IPv6. For this to work the ports need to match the ports you have selected when publishing the container-ports.

    (If you are having trouble, make sure you don't have expose_via_ipv6 set to false in your wider-world-to-container rule.)

Example: webserver reachable via IPv6

Let's assume you want to run a webserver as a Docker container and want ports 80 for HTTP and 443 for HTTPS on your host to forward to this container. The container you use internally uses ports 8080 and 8443 for HTTP and HTTPS respectively.

The following is how you have to configure the container:

$ docker run \
    --name "your_container" \
    --network "your_network" \
    --publish 80:8080 \
    --publish 443:8443 \
    ...

This is how you'd configure your rule:

[[wider_world_to_container.rules]]
network = "your_network"
dst_container = "your_container"
expose_port = [
    "80:8080",
    "443:8443",
]

The result of this is that your container will be reachable from the host-ports 80 and 443, from both IPv4 and IPv6.

Supported Docker versions

At least Docker 1.13.0 is required.

DFW is continuously and automatically tested with the following stable Docker versions (using the latest patch-version each):

  • 19.03
  • 18.09
  • 18.06
  • 18.03
  • 17.12
  • 17.09
  • 17.07
  • 17.06
  • 1.13

License

DFW is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in DFW by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~18MB
~366K SLoC