
BPF port-based firewall for systemd services - Klasiaster
https://kailueke.gitlab.io/systemd-bpf-firewall-loader/
======
ldng
Maybe someone can enlighten me because I'm failing to see the relevance of
SystemD here.

So the idea is, instead of having a central firewall managing all the host
rules, each service define it's own firewall policy ? How do I override a
policy ?

I maybe missing something but somehow I'm not sure it's the right place to do
this.

I'll end up joining the camp of SystemD does too much and breaks a lot of
POSIX semantics making Linux systems hard to debug.

Lately it's been getting more and more in my way. Things that I have problems
with lately, DNS, cgroup and namespace. Every time I've lost a considerable
amount of time because of poorly documented and mostly unexpected SystemD
behavior. Color me annoyed.

Edit: Hum, well, wasn't supposed to but it end up into a rant

~~~
Klasiaster
Systemd creates cgroups and you can attach BPF filters with the new
`IPIngressFilterPath` option. It's also possible with some
`ExecStartPre=bpftool …` hack but it's not so nice because it's racy.

The value is that the service configuration knows which exact network behavior
a service has. The global iptables state is not context aware unless you tag
things by PID. And anyway it's a cleaner approach to bundle the firewall with
the service instead of manipulating global iptables state.

You can override the BPF firewall by adding a drop-in service file which
either appends an additional filter with `IPIngressFilterPath=filter` or
deletes all previously configured filters with `IPIngressFilterPath=`.

~~~
ldng
Really, what is the goal and when the feature creep ends ? At Kubernetes
certification ?

As much as I loved the unit conf + socket activation concept at first and
supported SystemD. It's become and unruly teenager. It has severely hindered
my productivity on several occasion but I'm stuck with it on Debian. One more
glitch and I'll probably start hating it's guts badly...

~~~
tomatocracy
I agree (and I'd say the low point was when I upgraded a laptop to buster and
silently ended up with systemd-resolved ignoring my local LAN DNS to try to
forward everything to 8.8.8.8 so couldn't resolve local hostnames).

However, you're not stuck with systemd on Debian. Install sysvinit-core and
remove systemd-sysv, and install libpam-elogind and elogind instead of libpam-
systemd if you need it. You'll very likely still end up with some of the
libraries installed - it's not the end of the world and you can install Devuan
if you really don't want that, but systemd won't be running as PID 1 and you
don't get rubbish like journald or resolved.

~~~
ldng
On my personal machine I might end up doing just that. But that can not always
be changed in working environment.

Edit : Also been bitten by the DNS mingling.

------
arianvanp
This approach is very similar to what
[https://github.com/cilium/cilium](https://github.com/cilium/cilium) is doing
for containers right? I wonder if it would be easy to reuse the battle-tested
bpf programs that cilium provides and load them into systemd units.

There is more crazy shit that we can do. Like set up entire service meshes
with load balancers for your systemd units. Very neat.

~~~
eloycoto
yes, it uses the same technology.

Snabb I think that also has XDP/BPF support and you can do similar things
using Lua

------
nailer
If you're wondering what BPF is: [https://linux-audit.com/bpfilter-next-
generation-linux-firew...](https://linux-audit.com/bpfilter-next-generation-
linux-firewall/) and
[https://en.wikipedia.org/wiki/Berkeley_Packet_Filter](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter)

~~~
cgh
Many people are surprised to learn that Linux (and I guess some of the BSDs?)
have a virtual machine that runs in the kernel and executes its own specific
bytecode. LLVM has a BPF backend, as mentioned in this article.

~~~
bifrost
Pretty sure BPF originated in BSD land :)

Prior to BPF there was "Enet Packet Filter", then Ultrix Packet Filter then
something under SunOS before it became BPF. BPF was created in 1990 (at
Berkeley) which was widely BSD oriented.

------
justicezyx
Isn't the BPF program has to be attached with root privilege? If so, the idea
that to have per service is not enforceable anyway, right? As potential my
filter can affect any other process running on the host.

------
ausjke
what's the user case for this and how does this complements iptables? why do I
need this?

BPF is very interesting, I remember one thing is that it's of very small size
and has no loops, but I don't understand its use case for firewall yet.

~~~
arianvanp
This is a good talk about the how's and why's of BPF as a firewall:
[https://youtu.be/_Iq1xxNZOAo](https://youtu.be/_Iq1xxNZOAo)

------
cat199
sooo.. tcpwrappers?

------
skywhopper
Compiling dynamically generated C programs on demand to provide packet
filtering? It's clever but not in a good way. This is a really dangerous
approach to solving this problem.

~~~
alexgartrell
First, there's no need to do this "on demand." You can ship a .o around and
load it with bpftool. My team does a version of this at Facebook (using libbpf
directly) and it works really well. The big use case for on-demand programming
is in tracing where you want to capture structs, but BTF and Compile-once,
run-everywhere will mitigate this need [0]

Second, I don't actually agree. JIT compilation is a generally accepted
approach -- who cares what the IR is? And the bpf runtime is ULTRA
constrained, so you can't slip a `system("rm -rf /")` in there (by a long
shot).

[0]
[https://www.kernel.org/doc/html/latest/bpf/btf.html](https://www.kernel.org/doc/html/latest/bpf/btf.html)

~~~
jdc
I'm thoroughly skeptical that general acceptance means that JIT compilation
doesn't increase the attack surface of the stack.

~~~
Dylan16807
You'd need an interpreter at least, to avoid some kind of hideously complex
system.

So it's a question of interpreter vs. JIT, and a JIT makes it more feasible to
use an ultra-simple language with aggressive verification, without losing too
much speed.

Every feature has an attack surface, but you have to compare against the
alternative, not the lack of feature.

(This assumes that you can't force it to use a kernel interpreter despite the
JIT existing. Otherwise perhaps _that_ is the part that should be disabled.)

