Skip to content

Commit 108b1b2

Browse files
committed
New post
Cross posted from the original done for Addiva Elektronik, March 12. https://addiva-elektronik.github.io/2024/03/12/firewall-container/ Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 79b27d1 commit 108b1b2

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
---
2+
title: Firewall Container
3+
author: troglobit
4+
date: 2024-03-12 08:08:41 +0100
5+
categories: [showcase]
6+
tags: [container, containers, networking, firewall, docker, podman]
7+
---
8+
9+
![Docker whale](/assets/img/docker.webp){: width="200" .right}
10+
11+
This is the third post in a series about containers in Infix. This time
12+
we dive into using containers as "apps" to extend the system for custom
13+
firewall setups. Infix does not yet have native support for setting up
14+
firewalls or ACLs, so the approach is useful even for more basic setups.
15+
16+
See the [first post][1] for a background and networking basics.
17+
18+
> This post assumes knowledge and familiarity with the [Infix Network
19+
> Operating System](https://kernelkit.github.io/). Ensure you have
20+
> either a network connection or console access to your Infix system and
21+
> can log in to it using SSH. Recommended reading includes the
22+
> [networking documentation][0].
23+
{: .prompt-info }
24+
25+
----
26+
27+
28+
## Introduction
29+
30+
A clever feature of containers in Linux is that any namespace[^1] used
31+
to "contain" a process can be either unique to the process, a set of
32+
processes sharing the same container, or shared between containers.
33+
Infix limits this flexibility for containers to:
34+
35+
![](/assets/img/firewall.svg){: width="200" .right}
36+
37+
1. Fully contained, e.g., unique network namespace, fully isolated
38+
2. Shared network namespace with the host
39+
40+
The second one is what we'll focus on in this blog post because it
41+
allows us to add functionality to Infix that extends its networking
42+
capabilities.
43+
44+
The application in question is an [nftables][5] container, again from
45+
the [curiOS][2] project, that we will use to create basic router setup
46+
with NAT[^2] firewall.
47+
48+
> [nftables][5] is the Linux firewall, [see below](#example-etcnftablesconf)
49+
> for an `nftables.conf` example.
50+
{: .prompt-tip }
51+
52+
![](/assets/img/basic-router.svg)
53+
_**Figure 1:** Basic router, single WAN port, LAN ports bridged._
54+
55+
## Configuration
56+
57+
The Infix configuration consists of two parts: networking setup and the
58+
container. We start with the networking, we want a single port as our
59+
WAN port, connected to the Internet, all the other ports are bridged
60+
in a LAN configuration.
61+
62+
```console
63+
admin@infix:/> configure
64+
admin@infix:/config/> set dhcp-client client-if eth0
65+
admin@infix:/config/> edit interface br0
66+
admin@infix:/config/interface/br0/> set ipv4 address 192.168.0.1 prefix-length 24
67+
admin@infix:/config/interface/br0/> end
68+
admin@infix:/config/> set interface eth1 bridge-port bridge br0
69+
admin@infix:/config/> set interface eth2 bridge-port bridge br0
70+
admin@infix:/config/> set interface eth3 bridge-port bridge br0
71+
admin@infix:/config/> leave
72+
```
73+
74+
Time for the container configuration. This time we don't need to mark
75+
any interfaces as `container-network`. The key configuration settings
76+
are `set network host`, which tells Infix to share its network namespace
77+
with the container, and *privileged* mode.
78+
79+
```console
80+
admin@infix:/> configure
81+
admin@infix:/config> edit container nftables
82+
admin@infix:/config/container/nftables/> set image docker://ghcr.io/kernelkit/curios-nftables:24.02.0
83+
admin@infix:/config/container/nftables/> set network host
84+
admin@infix:/config/container/nftables/> set privileged true
85+
admin@infix:/config/container/nftables/> edit mount nftables.conf
86+
admin@infix:/config/container/nftables/mount/nftables.conf/> set target /etc/nftables.conf
87+
admin@infix:/config/container/nftables/mount/nftables.conf/> text-editor content
88+
... interactive editor starts up where you can paste your rules ...
89+
admin@infix:/config/container/nftables/mount/nftables.conf/> leave
90+
```
91+
92+
Notice the command `text-editor content`, it opens up an interactive
93+
[text editor][4] where you can paste in the contents of the file
94+
`/etc/nftables.conf` that will be mounted in the container.
95+
96+
The `text-editor` command base64 encodes the content and stores it
97+
in the Infix `startup-config`, thus ensuring that the configuration
98+
of the firewall is retained even when reconfigure the container, or
99+
upgrade the container image.
100+
101+
## The Result
102+
103+
We should now have a running container.
104+
105+
```console
106+
admin@infix:/> show container
107+
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
108+
aae7178c00f8 ghcr.io/kernelkit/curios-nftables:24.02.0 /usr/sbin/nft-hel... 56 seconds ago Up 56 seconds nftables
109+
```
110+
111+
This particular container cannot be connected to, like in the previous
112+
post, because it is only a plain application container with no embedded
113+
shell included. But we can exit to our host's shell and inspect the
114+
kernel netfilter rules:
115+
116+
```console
117+
admin@infix:/> exit
118+
admin@infix:~$ sudo nft list ruleset
119+
table ip filter {
120+
chain wan {
121+
icmp type echo-request limit rate 5/second accept
122+
}
123+
124+
chain lan {
125+
icmp type echo-request accept
126+
ip protocol . th dport vmap { tcp . 22 : accept, tcp . 53 : accept, udp . 53 : accept, udp . 67 : accept }
127+
}
128+
129+
chain input {
130+
type filter hook input priority filter; policy drop;
131+
ct state vmap { invalid : drop, established : accept, related : accept }
132+
iifname vmap { "eth0" : jump wan, "lo" : accept, "br0" : jump lan }
133+
}
134+
135+
chain forward {
136+
type filter hook forward priority filter; policy drop;
137+
ct state vmap { invalid : drop, established : accept, related : accept }
138+
iifname "br0" accept
139+
}
140+
}
141+
table ip nat {
142+
chain prerouting {
143+
type nat hook prerouting priority filter; policy accept;
144+
}
145+
146+
chain postrouting {
147+
type nat hook postrouting priority srcnat; policy accept;
148+
ip saddr 192.168.0.0/24 oif "eth0" masquerade
149+
}
150+
}
151+
```
152+
153+
> Here we run the `nft` program shipped with Infix. It is of course
154+
> possible to run the `nft` binary from the container, albeit not as
155+
> easily:
156+
>
157+
> ```shell
158+
> admin@infix:~$ sudo podman run --network=host --privileged=true --entrypoint=/usr/sbin/nft ghcr.io/kernelkit/curios-nftables:24.02.0 list ruleset
159+
> ```
160+
{: .prompt-tip }
161+
162+
## Example /etc/nftables.conf
163+
164+
This is the example firewall configuration referred to above.
165+
If your interface names or IP address plan differs, make sure
166+
to modify the file to match.
167+
168+
```
169+
define WAN = eth0
170+
define LAN = br0
171+
define NET = 192.168.0.0/24
172+
173+
# Drop all rules to allow reloading this file after edit
174+
flush ruleset
175+
176+
table ip filter {
177+
chain wan {
178+
# Accept ping for diagnostics, with rate limit
179+
icmp type echo-request limit rate 5/second accept
180+
181+
# allow SSH connections from some well-known internet host
182+
#ip saddr 81.209.165.42 tcp dport ssh accept
183+
}
184+
185+
chain lan {
186+
icmp type echo-request accept
187+
188+
# allow DHCP, DNS and SSH from the private network
189+
ip protocol . th dport vmap {
190+
tcp . 22 : accept,
191+
udp . 53 : accept,
192+
tcp . 53 : accept,
193+
udp . 67 : accept
194+
}
195+
}
196+
197+
chain input {
198+
type filter hook input priority 0; policy drop;
199+
200+
# Allow traffic from established and related packets, drop invalid
201+
ct state vmap {
202+
established : accept,
203+
related : accept,
204+
invalid : drop
205+
}
206+
207+
# allow loopback traffic, anything else jump to chain for further evaluation
208+
iifname vmap {
209+
lo : accept,
210+
$WAN : jump wan,
211+
$LAN : jump lan
212+
}
213+
214+
# the rest is dropped by the above policy
215+
}
216+
217+
chain forward {
218+
type filter hook forward priority 0; policy drop;
219+
220+
# Allow traffic from established and related packets, drop invalid
221+
ct state vmap {
222+
established : accept,
223+
related : accept,
224+
invalid : drop
225+
}
226+
227+
# connections from LAN to the internet or to other intranets are allowed
228+
iifname $LAN accept
229+
230+
# the rest is dropped by the above policy
231+
}
232+
}
233+
234+
table ip nat {
235+
chain prerouting {
236+
type nat hook prerouting priority filter; policy accept;
237+
}
238+
239+
chain postrouting {
240+
type nat hook postrouting priority srcnat; policy accept;
241+
ip saddr $NET oif $WAN masquerade
242+
}
243+
}
244+
```
245+
246+
247+
## Fin
248+
249+
That concludes the third post about containers in Infix. As usual,
250+
remember to
251+
252+
```console
253+
admin@infix:/> copy running-config startup-config
254+
```
255+
256+
Take care! <3
257+
258+
----
259+
260+
[^1]: Linux namespaces partition system resources such that one set of
261+
processes sees one set of resources, while another set of processes
262+
sees a different set of resources. There are many types: mount,
263+
user, PID, network, ...
264+
265+
[^2]: NAT firewall, in this context an RFC 2636 NAPT, or IP masquerading
266+
firewall with filtering of incoming traffic. For more information, see
267+
the excellent [Wikipedia article][3] on the topic.
268+
269+
[0]: https://github.com/kernelkit/infix/blob/main/doc/networking.md
270+
[1]: /posts/containers/
271+
[2]: https://github.com/kernelkit/curiOS/
272+
[3]: https://en.wikipedia.org/wiki/Network_address_translation
273+
[4]: https://github.com/kernelkit/infix/blob/main/doc/cli/text-editor.md
274+
[5]: https://wiki.nftables.org/wiki-nftables/index.php/Main_Page

assets/img/basic-router.svg

Lines changed: 4 additions & 0 deletions
Loading

assets/img/firewall.svg

Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)