{"id":13441517,"url":"https://github.com/mk-fg/systemd-cgroup-nftables-policy-manager","last_synced_at":"2025-10-30T09:06:44.734Z","repository":{"id":144996519,"uuid":"399027397","full_name":"mk-fg/systemd-cgroup-nftables-policy-manager","owner":"mk-fg","description":"Tool to add/update nftables cgroupv2 rules for systemd-managed unit cgroups (slices, services, scopes)","archived":false,"fork":false,"pushed_at":"2025-01-22T06:11:37.000Z","size":67,"stargazers_count":13,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-23T18:37:28.366Z","etag":null,"topics":["cgroups","firewall","network","nftables","nim","systemd"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mk-fg.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-08-23T08:24:57.000Z","updated_at":"2025-01-22T06:11:41.000Z","dependencies_parsed_at":"2023-12-29T01:26:03.181Z","dependency_job_id":"735f8f4d-dce1-4336-a735-eb778341834b","html_url":"https://github.com/mk-fg/systemd-cgroup-nftables-policy-manager","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fsystemd-cgroup-nftables-policy-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fsystemd-cgroup-nftables-policy-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fsystemd-cgroup-nftables-policy-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mk-fg%2Fsystemd-cgroup-nftables-policy-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mk-fg","download_url":"https://codeload.github.com/mk-fg/systemd-cgroup-nftables-policy-manager/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065281,"owners_count":21041872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cgroups","firewall","network","nftables","nim","systemd"],"created_at":"2024-07-31T03:01:34.974Z","updated_at":"2025-10-30T09:06:44.724Z","avatar_url":"https://github.com/mk-fg.png","language":"Nim","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"systemd cgroup (v2) nftables policy manager\n===========================================\n\n.. contents::\n  :backlinks: none\n\nThis repository URLs:\n\n- https://github.com/mk-fg/systemd-cgroup-nftables-policy-manager\n- https://codeberg.org/mk-fg/systemd-cgroup-nftables-policy-manager\n- https://fraggod.net/code/git/systemd-cgroup-nftables-policy-manager\n\n\n\nDescription\n-----------\n\nSmall tool that adds and updates nftables_ cgroupv2 filtering rules for\nsystemd_-managed per-unit cgroups (slices, services, scopes).\n\n\"cgroupv2\" is also often referred to as \"unified cgroup hierarchy\" (considered\nstable in linux since 2015), works differently from old cgroup implementation,\nand is the only one supported here.\n\nSimilar capability have also been added to systemd versions 255+ (2023-12-06 and\nlater) via NFTSet= option in unit files (see `\"man systemd.resource-control\"`_),\nbut its use is limited to system units (can't be used in ``~/.config/systemd/user``\nsession units).\n\nThis tool is somewhat redundant with that functionality, but can still be useful\nfor user session units, or if NFTSet= doesn't work for some purpose/reason.\n\n.. _nftables: https://nftables.org/\n.. _systemd: https://systemd.io/\n.. _\"man systemd.resource-control\":\n  https://man.archlinux.org/man/systemd.resource-control.5\n\n\nProblem that it addressess\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nnftables supports \"socket cgroupv2\" matching in rules (since linux-5.13+),\nsimilar to iptables' \"-m cgroup --path ...\", which can be used to add rules\nlike this::\n\n  add rule inet filter output socket cgroupv2 level 5 \\\n    \"user.slice/user-1000.slice/user@1000.service/app.slice/myapp.service\" accept\n\n(or in iptables: ``iptables -A OUTPUT -m cgroup --path ... -j ACCEPT``)\n\nBut when trying to put this into /etc/nftables.conf, it will fail to load on boot\n(same as similar iptables rules), as that \"myapp.service\" cgroup with a long\npath does not exist yet.\n\nBoth nftables/iptables rules use xt_cgroup kernel module that - when looking at\nthe packet - actually matches numeric cgroup ID, and not the path string, and\ndoes not update those IDs dynamically when cgroups are created/removed in any way.\n\nThis means that:\n\n- Firewall rules can't be added for not-yet-existing cgroups.\n\n  Causes \"Error: cgroupv2 path fails: No such file or directory\" from \"nft\"\n  command and \"xt_cgroup: invalid path, errno=-2\" error in dmesg for iptables.\n\n- If cgroup gets removed and re-created, none of the existing rules will apply to it.\n\n  This is because new cgroup gets a new unique ID, which can't be present in any\n  pre-existing netfilter tables, so none of the rules will match it.\n\nSo basically such rules in a system-wide policy-config only work for cgroups\nthat are created early on boot and never removed after that.\n\nThis is not what happens with most systemd services and slices, restarting which\nwill also re-create cgroups, and which are usually started way after system\nfirewalls are initialized (and often can't be started on boot - e.g. user units).\n\n\nSolution:\n~~~~~~~~~\n\nSince this tool was written, ``NFTSet=`` directive was added to systemd,\nwhich mostly addresses this for system units already - use that if possible,\nand see caveats section below for some of potential shortcomings there.\n\nMonitor cgroup (or systemd unit) creation/removal events and (re-)apply any\nrelevant rules to these dynamically.\n\nThis is `how \"socket cgroupv2\" matcher in nftables is intended to work`_::\n\n  Following the decoupled approach: If the cgroup is gone, the filtering\n  policy would not match anymore. You only have to subscribe to events\n  and perform an incremental updates to tear down the side of the\n  filtering policy that you don't need anymore. If a new cgroup is\n  created, you load the filtering policy for the new cgroup and then add\n  processes to that cgroup. You only have to follow the right sequence\n  to avoid problems.\n\nSo that's pretty much what this simple tool does, subscribing to systemd unit\nstart/stop events via journal (using libsystemd) and updating any relevant rules\non events from there (using libnftables).\n\n.. _how \"socket cgroupv2\" matcher in nftables is intended to work:\n  https://patchwork.ozlabs.org/project/netfilter-devel/patch/1479114761-19534-1-git-send-email-pablo@netfilter.org/#1511797\n\n\nIntended use-case:\n~~~~~~~~~~~~~~~~~~\n\nDefining system-wide policy to whitelist connections to/from specific systemd\nunits (can be services/apps, slices of those, or ad-hoc scopes) in an easy and\nrelatively foolproof way.\n\nI.e. if a desktop system is connected to some kind of \"intranet\" VPN, there's\nno reason for random complex and leaky apps like web browsers or games to be able\nto connect to anything there (think fetch() JS call from any site you visit),\nand that is trivial to block with a single firewall rule.\n\nThis tool is intended to manage a whitelist of rules for systemd units on top,\nthat should have access there, and hence are allowed to bypass such rule.\n\nAgain, systemd has aforementioned NFTSet= option, as well as network filtering\nvia eBPFs attached to cgroups (IPAddressAllow/Deny=, BPFProgram=, IPEgressFilterPath=\nand such), which can be used as an alternative to this tool.\n\n\n\nBuild / Install\n---------------\n\nThis is a small Nim_ command-line app, can be built with any modern\n`Nim compiler`_, e.g. using included Makefile::\n\n  % make\n  % ./scnpm --help\n  Usage: ./scnpm [opts] [nft-configs ...]\n  ...\n\n(or run ``nim c -d:release -d:strip -d:lto_incremental --opt:size scnpm.nim`` without make)\n\nThat should produce ~150K binary, linked against libsystemd (for journal access)\nand libnftables (to re-apply cgroupv2 nftables rules), which can then be installed\nand copied between systems normally.\nNim compiler is only needed to build the tool, not to run it.\n\nscnpm.service_ systemd unit file can be used to auto-start it on boot.\n\nJournal is used as an event source instead of more conventional dbus signals to\nbe able to monitor state changes of units under all \"systemd --user\" instances\nas well as system ones, which are sent through multiple transient dbus brokers,\nso much more difficult to reliably track there.\n\n.. _Nim: https://nim-lang.org/\n.. _Nim compiler: https://nim-lang.org/install_unix.html\n.. _scnpm.service: scnpm.service\n\n\n\nUsage\n-----\n\nTool is designed to parse special commented-out rules for it from the same\nnftables.conf as used with the rest of ruleset, for consistency\n(though of course they can be stored in any other file(s) as well)::\n\n  ## Allow connections to smtp over vpn for system postfix.service\n  # postfix.service :: add rule inet filter vpn.whitelist \\\n  #   socket cgroupv2 level 2 \"system.slice/postfix.service\" tcp dport 25 accept\n\n  ## Allow connections to intranet mail for a scope unit running under \"systemd --user\"\n  ## \"systemd-run\" can be used to easily start apps in custom scopes or slices\n  # app-mail.scope :: add rule inet filter vpn.whitelist socket cgroupv2 level 5 \\\n  #   \"user.slice/user-1000.slice/user@1000.service/app.slice/app-mail.scope\" \\\n  #   ip daddr mail.intranet.local tcp dport {25, 143} accept\n\n  ## Only allow whitelisted apps to connect over \"my-vpn\" iface\n  add rule inet filter output oifname my-vpn jump vpn.whitelist\n  add rule inet filter output oifname my-vpn drop\n\nCommented-out \"add rule\" lines would normally make this config fail to apply on\nboot, as those service/scope/slice cgroups won't exist yet at that point in time.\n\nScript will parse those \"\u003cunit-to-watch\u003e :: \u003crule\u003e\" comments, and try to apply\nrules from them on start and whenever any kind of state-change happens to a unit\nwith the name specified there.\n\nFor example, when postfix.service is stopped/restarted with the config above,\ncorresponding vpn.whitelist rule will be removed and re-added, allowing access\nto a new cgroup which systemd will create for it after restart.\n\nTo start it in verbose mode: ``./scnpm --flush --debug /etc/nftables.conf``\n\n``-f/--flush`` option will purge (flush) all chains mentioned in the rules\nthat will be monitored/applied on tool start, so that leftover rules from any\nprevious runs are removed, and can be replaced with more fine-grained manual\nremoval if these are not dedicated chains used for such dynamic rules only.\n\nRunning without ``-d/--debug`` should not normally produce any output, unless\nthere are some (non-critical) warnings like unexpected mismatch or nftables error,\ncode bugs or fatal errors.\n\nStarting the tool on boot should be scheduled after nftables.service,\nso that ``--flush`` option will be able to find all required chains,\nand will exit with an error otherwise.\n\nMultiple nftables rules linked to same systemd unit(s) are allowed.\n\nChanges in parsed config files are not auto-detected, and only applied by\neither sending SIGHUP or tool restart, which can be done manually after changes,\nconfigured in nftables.service (e.g. via PropagatesReloadTo= and/or BindsTo=)\nor systemd.path unit monitoring state of source configuration file(s);\nor - without signal - using ``-u/--reload-with-unit`` or ``-a/--reapply-with-unit``\nopts, since this tool monitors systemd unit states anyway, and can spot when\nthings restart there on its own.\n\nSyntax errors in nftables rules should produce warnings when these are applied on\ntool start or changes, so should be hard to miss, but maybe do check \"nft list chain\"\nor debug output when rules are supposed to be enabled after conf updates anyway.\n\nTo modify nftables rulesets, CAP_NET_ADMIN capability is required, which can be\npassed via AmbientCapabilities= in systemd service (or similar option in capsh)\nin addition to SupplementaryGroups=systemd-journal and netlink access to avoid\nrunning this as full root.\n\n\n\nCaveats and limitations\n-----------------------\n\n- Due to \"best-effort\" nature of trying to apply rules when unit startup is\n  detected, and an inherent race condition between systemd creating\n  service/cgroup and rule being applied, I'd heavily recommend to always use\n  allow-listing rules with this tool, which fail on the safe side.\n\n- I think \"cgroupv2\" in nftables rule must be the one where network socket was\n  created, and not the one where systemd might move the process using it.\n\n  So for incoming ssh connections for example, \"sshd-session\" process might\n  end up in session-N.scope under user.slice, but nftables will only match it\n  as belonging to sshd.service cgroup, so some rules might need to have different\n  cgroup string in the rule than a name that triggers the rule to the left of it.\n\n  Not 100% sure that's how it works or supposed to work, but have observed it earlier.\n\n- Use HUP signal, ``-u/--reload-with-unit`` (same as SIGHUP) or ``-a/--reapply-with-unit``\n  option to restore transient cgroup-specific rules after nftables restart\n  or other firewall resets that'd remove those.\n\n\n\nLinks\n-----\n\n- `systemd.resource-control(5)`_ manpage that describes implementation of\n  similar functionality there - lookup ``NFTSet=`` option.\n\n- `Systemd firewall integration suggestions (issue #7327)`_ - more comprehensive\n  netfilter integration than NFTSet= option above, still at a proposal/suggestion\n  stage at the moment (2025-04-10), neither accepted nor rejected.\n\n- `helsinki-systems/nft_cgroupv2`_ - alternative third-party implementation of\n  such matching in nftables.\n\n  AFAICT it doesn't rely on cgroup id's and instead resolves these from cgroup\n  path for every packet, which is probably not great wrt performance, but might\n  be ok for most use-cases where conntrack filters-out traffic before these rules.\n\n  Might conflict with current upstream nftables implementation due to \"cgroupv2\"\n  keyword used there as well.\n\n- `Upstreamed \"netfilter: nft_socket: add support for cgroupsv2\" patch\n  \u003chttps://patchwork.ozlabs.org/project/netfilter-devel/patch/20210426171056.345271-3-pablo@netfilter.org/\u003e`_\n  for \"cgroupv2\" matching support in nftables (0.99+) on the linux kernel side (linux-5.13+).\n\n- `\"netfilter: implement xt_cgroup cgroup2 path match\" patch\n  \u003chttps://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=c38c4597\u003e`_\n  from linux-4.5.\n\n- Earlier version of this tool was written in OCaml_, and can be last found in `commit\n  048a8128 \u003chttps://github.com/mk-fg/systemd-cgroup-nftables-policy-manager/tree/048a8128\u003e`_.\n\n.. _systemd.resource-control(5): https://man.archlinux.org/man/systemd.resource-control.5\n.. _Systemd firewall integration suggestions (issue #7327):\n  https://github.com/systemd/systemd/issues/7327\n.. _helsinki-systems/nft_cgroupv2: https://github.com/helsinki-systems/nft_cgroupv2/\n.. _OCaml: https://ocaml.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk-fg%2Fsystemd-cgroup-nftables-policy-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmk-fg%2Fsystemd-cgroup-nftables-policy-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmk-fg%2Fsystemd-cgroup-nftables-policy-manager/lists"}