The DFWFW configuration file is JSON formatted with a hash as root node, which might contain the following keys:
- docker_socket: Specification of the Docker socket. Default value is
http:/var/run/docker.sock/
. (Note the trailing slash! Details) - log_path: JSON string, by default it is /var/log/dfwfw. If
log_split_by_event
is true, this path should be a directory, else a complete filename. - log_split_by_event: JSON boolean, default true. If it is turned on, DFWFW splits the log files by event (for example signals, Docker container events).
- external_network_interface: See
external_network_interface_definition
- initialization: Initial firewall rules for the host.
- container_to_container: Container to container firewall rules
- container_to_wider_world: Container to wider world rules
- container_to_host: Container to host rules
- wider_world_to_container: Wider world to container rules
- container_dnat: DNAT rules for containers
- container_internals: Container internal rules
- container_aliases: Container aliases
A simple substitution based preprocessor layer is invoked before parsing the string with the JSON object. It supports stripping out of comments (lines prefixed with hashmark).
It also supports the following commands with the @|key: value|
syntax:
- including additional files, example:
@|include: /etc/dfwfw/conf.d/*.json|
- embedding IP address of the specified network interface, example:
@|interface_ip: eth0|
DFWFW tries to open this configuration file from the following location (in that order):
- /etc/dfwfw/dfwfw.conf
- /etc/dfwfw.conf
The initialization key takes JSON hashes of the supported netfilter tables. Their values are JSON arrays with the actual firewall rules you want to have as your initial configuration. Upon startup, DFWFW commits these rules to netfilter.
If you create the DFWFW_INPUT/DFWFW_FORWARD/etc. chains and also rules jumping to them, then DFWFW won't create them by itself (which is the default behavior).
For example, a useful set of initial rules would look like this:
"initialization": {
"filter": [
":DFWFW_INPUT - [0:0]",
":HOST_OUTBOUND - [0:0]",
":HOST_INCOMING - [0:0]",
"-P INPUT DROP",
"-F INPUT",
"-A INPUT -m state --state INVALID -j DROP",
"-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT",
"-A INPUT -j DFWFW_INPUT",
"-A INPUT -m state --state NEW -j HOST_INCOMING",
"-F HOST_INCOMING",
"-A HOST_INCOMING -p tcp --dport 22 -j ACCEPT",
"-A HOST_INCOMING -p icmp -j ACCEPT",
"-P OUTPUT DROP",
"-F OUTPUT",
"-A OUTPUT -m state --state INVALID -j DROP",
"-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT",
"-A OUTPUT -m state --state NEW -j HOST_OUTBOUND",
"-F HOST_OUTBOUND",
"-A HOST_OUTBOUND -p udp --dport 53 -j ACCEPT",
"-A HOST_OUTBOUND -p tcp --dport 80 -j ACCEPT",
"-A HOST_OUTBOUND -p tcp --dport 443 -j ACCEPT",
"-A HOST_OUTBOUND -p icmp -j ACCEPT",
"-P FORWARD DROP"
]
},
In this example, we created and thus specified the location of DFWFW_INPUT chain among the INPUT chain rules.
We did not touch the DFWFW_FORWARD chain, letting DFWFW create it upon startup.
Note, that we did not open additional ports on HOST_INCOMING, since we intend to run only the SSH service on the host,
all other services would be dockerized, their firewall rules are covered in the wider_world_to_container
section.
You can specify rules to affect communication between Docker containers. By design, it is not possible to grant communication between containers not being on the same network.
The following keys can be specified inside container_to_container
:
- default_policy: see
default_policy_definition
- rules: array of
container_to_container_rule_definition
The following keys can be specified inside container_to_container_rule_definition
:
- network: see
network_definition
- src_container: optional, see
container_definition
- dst_container: optional, see
container_definition
- src_dst_container: optional, but exclusive to src and dst_container. see
container_src_dst_definition
- filter: optional string, additional iptables filters like
-p tcp --dport 25
- action: see
action_definition
You can specify rules affecting the communication originating inside a container towards the wider world.
As of 1.10, Docker does not offer a way to configure the default gateway. One of the connected networks will be selected randomly.
For this reason, specifying network is optional in container_to_wider_world
category.
The following keys can be specified inside container_to_wider_world
:
- default_policy: see
default_policy_definition
- rules: array of
container_to_wider_world_rule_definition
The following keys can be specified inside container_to_wider_world_rule_definition
:
- network: optional, default value is "Name =~ .*". See also
network_definition
- src_container: optional, see
container_definition
- filter: optional string, additional iptables filters like
-p tcp --dport 25
- action: see
action
definition - external_network_interface: optional, see external_network_interface_definition
Note:
By specifying bridge
as network, you can configure dedicated rules for build-time containers.
You can specify rules affecting the communication originating inside a container towards services running directly on the host.
The following keys can be specified inside container_to_host
:
- default_policy: see
default_policy_definition
- rules: array of
container_to_host_rule_definition
The following keys can be specified inside container_to_wider_world_rule_definition
:
- network: see
network_definition
- src_container: optional, see
container_definition
- filter: optional string, additional iptables filters like
-p tcp --dport 25
- action: see
action
definition
Using wider_world_to_container rules you can make services running inside your containers accessible from the outside world. In case of IPv4, DFWFW configures DNAT rules in the background according to the configuration and also forward rules accepting the same ports. In case of IPv6, DFWFW configures forward rules accepting the specified ports (TODO).
The following keys can be specified inside wider_world_to_container
:
- rules: array of
wider_world_to_container_rule_definition
The following keys can be specified inside wider_world_to_container_rule_definition
:
- network: see
network_definition
- dst_container: see
container_definition
- expose_port: optional, see
expose_port_definition
- external_network_interface: optional, see external_network_interface_definition
Using the container_dnat
feature it is possible to define rules with destination network address translation.
This category is special, source and destination networks might be different.
In case of IPv4, DFWFW configures DNAT rules in the background according to the configuration and also
forward rules accepting the same ports.
In case of IPv6, DFWFW configures forward rules accepting the specified ports (TODO).
The following keys can be specified inside container_dnat
:
- rules: array of
container_dnat_rule_definition
The following keys can be specified inside container_dnat_rule_definition
:
- src_network: optional, see
network_definition
- src_container: optional, see
container_definition
- dst_network: see
network_definition
- dst_container: see
container_definition
- expose_port: see
expose_port_definition
The --to-destination parameter of the iptables rules will be generated based on the destination network/container defined in the actual rule.
Both dst_network and dst_container should be defined and should match only one container. This latter is not enforced, it just makes not too much sense if it matched many.
Using container_internals you can inject iptables rules into your containers for additional security.
DFWFW needs access to the host process namespace and also SYS_ADMIN
capability to be able to do this.
The following keys can be specified inside container_internals
:
- rules: array of
container_internals_rule_definition
The following keys can be specified inside container_internals_rule_definition
:
- container: see
container_definition
- table: optional, see
table
definition, default isfilter
- rules: see
container_internals_iptables_rule_definition
By using the container_alias feature you can instruct DFWFW to maintane fixed host name aliases for your containers.
This might be useful if you append some version information to your container names but you still want them to be discovered
via a fixed alias. (For example you want the container some-nginx-199
to be reachable via the nginx
host alias
from other containers of the same network.)
The following keys can be specified inside container_internals
:
- rules: array of
container_alias_rule_definition
The following keys can be specified inside container_alias_rule_definition
:
- aliased_container: see
container_definition
. E.g. Name =~ .nginx. - alias_name: optional, JSON string of the wanted alias (e.g.
my.nginx.alias
). The default value ofalias_name
is the name of the aliased container. - receiver_network: see
network_definition
- receiver_containers: see
container_definition
If alias_nameis omitted Hosts file in containers matching
receiver_network+
receiver_containerswill be updated to have
alias_namepointing to
aliased_container`.
The network key is a JSON string, which can be one the following:
- Name of a network. This is a syntax sugar of the following:
"Name == name_of_the_network"
- An expression of key operator value
The expression components:
- key can be:
Name
,IdShort
orId
- Name refers to the name of the network
- IdShort refers to the short (12 letters) ID of the network
- Id refers to the full ID of the network (so the full SHA256 string)
- operator can be:
==
,=~
or!~
==
: the equality operator=~
: matching via regular expression operator!~
: negated matching via regular expression operator
- value can be: anything you want your expression match. For regular expressions the backslashes must be escaped since the expression is parsed by a JSON library first.
An example:
"network": "Name =~ ^web(-testing)?$"
The container/src_container/dst_container key is a JSON string, which can be one the following:
- Name of a container. This is a syntax sugar of the following:
"Name == name_of_the_container"
- An expression of key operator value
The expression components:
- key can be:
Name
,IdShort
orId
- Name refers to the name of the container
- IdShort refers to the short (12 letters) ID of the container
- Id refers to the full ID of the container (so the full SHA256 string)
- operator can be:
==
,=~
or!~
==
: the equality operator=~
: matching via regular expression operator!~
: negated matching via regular expression operator
- value can be: anything you want your expression match. For regular expressions the backslashes must be escaped since the expression is parsed by a JSON library first.
An example:
"container": "Name =~ ^php-\\d+"
Refers to a (string) or an array of external network interfaces. If it is specified as an array, the first member will be used
for setting up the masquerade rule in the nat table.
The default value of the top level external_network_interface
node is eth0
.
The container_to_wider_world and wider_world_to_container rules will use this top level setting unless overridden in the rule
specification.
Examples:
"external_network_interface": "eth0"
"external_network_interface": ["eth0","eth1"]
This definition was introduced in order to make work easier with backreferences in regular expressions. The same keys and same operators can be used, but the value for actual test would be a pair of containers of the specified network. In case of a match, the two containers are treated as src and dst containers.
For the comparision a delimiter string of =>
is inserted between the two names.
For example the following two definitions are the same:
# rule #1:
{
"src_dst_container": "nginx=>apache"
}
# rule #2:
{
"src_container": "nginx",
"dst_container": "apache"
}
As mentioned above, the same key and operators can be used as with regular container definitions.
In the following example DFWFW would run a regular expression based test and match the traffic between containers called
nginx
and mono
:
"src_dst_container": "Name =~ (\\w)ginx=>mo\\g1o"
The expose_port
key can be a JSON string or array. If expose_port
is missing from a wider_world_to_container rule definition then DFWFW would iterate over all the matching containers and use their exposed port (the ones specified via the -p
flag on docker run
commandline or via the EXPOSE
command in the respective Dockerfile).
The string version can be one of the following versions:
- a numerical value: a TCP port number
- port_number slash protocol_specification: Examples:
8080
80/tcp
53/udp
The array version holds array of hashes with the following keys:
- host_port: port number on the host machine. Port range can be specified separated by a colon.
- container_port: optional, default is same as host_port. The port number inside the container where the service is listening.
- family: optional, protocol family, default is tcp
The table
key must be a JSON string holding one of the tables supported by iptables, such as: "filter", "nat", "raw" or "mangle".
The action
key is a JSON string with the following possible values:
- ACCEPT
- DROP
- REJECT
- LOG
Default policy is an action, so action definition applies here. If the defaul policy is specified, then a new rule is appended to the end of the chain with the following network definition:
"network": "Name =~ .*"
The action for this new rule is the same as default policy. This way DFWFW generates a category specific iptables rule on each network according to the specified default policy.