My wish for per-port IP access controls in systemd .service units

May 29, 2022

I tweeted:

I wish systemd offered a simple way to say 'only allow the following IP ranges to connect to port X of this service'. You can allow only some IP ranges, but then that applies to outgoing connections too and things like DNS queries done by the service.

[...]

There are lots of general Linux options to firewall access to a port, but AFAIK none of them are easily tied to a .service unit so that you always get the two together and can't deploy & activate the service without its firewall protection.

Suppose, not entirely hypothetically, that you need to deploy an instance of Prometheus Blackbox on some host in order to let your Prometheus setup see a network segment that it isn't normally connected to. It's not entirely safe to allow everyone inside your network to talk to Blackbox, which means that you want to restrict access to the port it listens on.

Systemd offers IPAddressAllow and IPAddressDeny as part of its general resource control, but when applied to .service units they affect all network activity performed by the service, not just connections to it. Since the purpose of Blackbox is to send network traffic to external things, you have to allow at least everything it needs to probe to talk to Blackbox, which may be less than ideal.

You can write firewall rules, but currently there's nothing that embeds firewall rules in systemd service units or ties the two together so that you can't deploy the service without the firewall. Well, I suppose you can add some ExecStartPre commands (and the corresponding ExecStopPost), but that gets awkward and it may not interact well with any other uses of Linux firewalling you're doing on the system. At the very least it feels like a hack, and it's not naturally modular the way that systemd .service units normally are, where you don't have to care about what other ones do.

According to the systemd documentation, you can do this if you use a .socket unit (with IP address restrictions on the socket unit but not the service unit). Unfortunately, not many things support systemd socket activation (and Blackbox isn't one of them). Also, I'm not sure how well a socket unit and socket activation works in general for socket services that are daemons and keep running forever.

(It's possible that firewalld will someday be sufficiently pervasive that it can serve as the general, modular IP access control system for things like that. This would allow more confidence that your .service manual firewall changes wouldn't collide with or be wiped out by something that another service was doing.)


Comments on this page:

By James (trs80) at 2022-06-11 07:20:11:

I mean, it sounds a bit like you want TCP Wrappers from back in the inetd days.

By cks at 2022-06-13 12:13:32:

That's pretty much it. I don't need all of the trimmings of TCP Wrappers, but I certainly would like the very basic bit of per-port IP block/allow, integrated with the application's general setup.

(TCP Wrappers had a lot of trimmings and some of them are the kind of things that are both out of favour and hard to implement outside the service itself, since they involved DNS lookups or even running other programs. But mostly we used the basics.)

Well here I strongly tend to have services running as their own user, using capabilities(7) when needed, so I then just restrict ports usage to specific users (something nftables allows, not sure about the other firewalling methods).

Written on 29 May 2022.
« It's a bit risky to give people access to your Prometheus Blackbox exporter
Systemd memory limits and strict memory overcommit »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sun May 29 21:39:35 2022
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.