Add sandboxing and strip privileges on Linux with systemd #146

Open
opened 2025-11-20 05:07:00 -05:00 by saavagebueno · 8 comments
Owner

Originally created by @hg on GitHub (Jun 30, 2022).

netbird is an awesome project, but I'm not a fan of how the client runs with full privileges without any restrictions at all. Not great for a networked service.

With a few systemd features, we can fix this without any modifications to the project's code.

I've been maintaining an unofficial package for Arch Linux. It has most of the isolation flags enabled (have a look at source files at the bottom of the page):

  • run the client under an ordinary user (with network capability set — it's not great, but much better than full root)
  • most of the FS tree is read-only or not available at all (places like /home or /proc)
  • netbird cannot load kernel modules, reboot the system, change mount points, etc.

See more suggestions with:

$ systemd-analyze security netbird

Ideally the default socket path should be changed to /var/run/netbird/netbird.sock because /var/run typically can only be written by root, and systemd can prepare a subdirectory for you with an appropriate owner (see the patch on AUR). On non-systemd systems it can be created by the installation script.

Any interest in adding something like this to the official .service so more users benefit from it?

Originally created by @hg on GitHub (Jun 30, 2022). netbird is an awesome project, but I'm not a fan of how the client runs with full privileges without any restrictions at all. Not great for a networked service. With a few systemd features, we can fix this without any modifications to the project's code. I've been maintaining an [unofficial package][aur] for Arch Linux. It has most of the isolation flags enabled (have a look at source files at the bottom of the page): - run the client under an ordinary user (with network capability set — it's not great, but much better than full root) - most of the FS tree is read-only or not available at all (places like `/home` or `/proc`) - netbird cannot load kernel modules, reboot the system, change mount points, etc. See more suggestions with: $ systemd-analyze security netbird Ideally the default socket path should be changed to `/var/run/netbird/netbird.sock` because `/var/run` typically can only be written by root, and systemd can prepare a subdirectory for you with an appropriate owner (see the patch on AUR). On non-systemd systems it can be created by the installation script. Any interest in adding something like this to the official `.service` so more users benefit from it? [aur]: https://aur.archlinux.org/packages/netbird
Author
Owner

@mlsmaycon commented on GitHub (Jul 2, 2022):

Hi @hg , thanks for your suggestion, this is something in our internal roadmap (we will move to the public roadmap)

We need to review current and short term needs of the agent and then we will apply.

@mlsmaycon commented on GitHub (Jul 2, 2022): Hi @hg , thanks for your suggestion, this is something in our internal roadmap (we will move to the public roadmap) We need to review current and short term needs of the agent and then we will apply.
Author
Owner

@misuzu commented on GitHub (Aug 15, 2022):

I'm too would like to see an official hardened systemd unit file so I'm 100% I won't break something.

netbird cannot load kernel modules, reboot the system, change mount points, etc.

Isn't it using wg kernel module though?

Ideally the default socket path should be changed to /var/run/netbird/netbird.sock because /var/run typically can only be written by root, and systemd can prepare a subdirectory for you with an appropriate owner (see the patch on AUR).

I had to do the same on NixOS.

@misuzu commented on GitHub (Aug 15, 2022): I'm too would like to see an official hardened systemd unit file so I'm 100% I won't break something. > netbird cannot load kernel modules, reboot the system, change mount points, etc. Isn't it using wg kernel module though? > Ideally the default socket path should be changed to /var/run/netbird/netbird.sock because /var/run typically can only be written by root, and systemd can prepare a subdirectory for you with an appropriate owner (see the patch on AUR). I had to do the same on NixOS.
Author
Owner

@misuzu commented on GitHub (Aug 15, 2022):

I can also suggest you to use systemd's DynamicUser and store config file under /var/lib/netdata since it's not really a config, but state.

@misuzu commented on GitHub (Aug 15, 2022): I can also suggest you to use systemd's DynamicUser and store config file under `/var/lib/netdata` since it's not really a config, but state.
Author
Owner

@sebastianschauenburg commented on GitHub (Apr 5, 2023):

This would solve my current usecase: running netbird via systemd, user level (via linger). With setcap you can easily add cap_net_admin and limit the access for the application, while being sudo-less

@sebastianschauenburg commented on GitHub (Apr 5, 2023): This would solve my current usecase: running netbird via systemd, user level (via `linger`). With `setcap` you can easily add `cap_net_admin` and limit the access for the application, while being sudo-less
Author
Owner

@sebastianschauenburg commented on GitHub (Apr 6, 2023):

Could not find a template file online for the systemd service file (would be great if it wouldn't be inside the app to be honest) , so decided to install the systemd service, to check out the generated files.

This is /etc/systemd/system/netbird.service:

[Unit]
Description=A WireGuard-based mesh network that connects your devices into a single private network.
ConditionFileIsExecutable=/home/user/Downloads/netbird/netbird

After=network.target syslog.target

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/home/user/Downloads/netbird/netbird "service" "run" "--config" "/etc/netbird/config.json" "--log-level" "info" "--log-file" "/var/log/netbird/client.log"
StandardOutput=file:/var/log/netbird/netbird.out
StandardError=file:/var/log/netbird/netbird.err
Restart=always
RestartSec=120
EnvironmentFile=-/etc/sysconfig/netbird

[Install]
WantedBy=multi-user.target

And then I checked out the json file /etc/netbird/config.json:

{
    "PrivateKey": "somesortofprivatekey",
    "PreSharedKey": "",
    "ManagementURL": {
        "Scheme": "https",
        "Opaque": "",
        "User": null,
        "Host": "[api.wiretrustee.com:443](http://api.wiretrustee.com:443/)",
        "Path": "",
        "RawPath": "",
        "OmitHost": false,
        "ForceQuery": false,
        "RawQuery": "",
        "Fragment": "",
        "RawFragment": ""
    },
    "AdminURL": {
        "Scheme": "https",
        "Opaque": "",
        "User": null,
        "Host": "[app.netbird.io:443](http://app.netbird.io:443/)",
        "Path": "",
        "RawPath": "",
        "OmitHost": false,
        "ForceQuery": false,
        "RawQuery": "",
        "Fragment": "",
        "RawFragment": ""
    },
    "WgIface": "wt0",
    "WgPort": 51820,
    "IFaceBlackList": [
        "wt0",
        "wt",
        "utun",
        "tun0",
        "zt",
        "ZeroTier",
        "wg",
        "ts",
        "Tailscale",
        "tailscale",
        "docker",
        "veth",
        "br-"
    ],
    "DisableIPv6Discovery": false,
    "SSHKey": "-----BEGIN PRIVATE KEY-----\nsomesortofsshprivatekeydata\n-----END PRIVATE KEY-----\n",
    "NATExternalIPs": null,
    "CustomDNSAddress": ""
}

Changing everything to user level will indeed not work. Apparently the socket is created and used (mandatory), which does not seem to be configurable.

As the OP states, it would be great if this could be enhanced.

@sebastianschauenburg commented on GitHub (Apr 6, 2023): Could not find a template file online for the systemd service file (would be great if it wouldn't be inside the app to be honest) , so decided to install the systemd service, to check out the generated files. This is `/etc/systemd/system/netbird.service`: [Unit] Description=A WireGuard-based mesh network that connects your devices into a single private network. ConditionFileIsExecutable=/home/user/Downloads/netbird/netbird After=network.target syslog.target [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStart=/home/user/Downloads/netbird/netbird "service" "run" "--config" "/etc/netbird/config.json" "--log-level" "info" "--log-file" "/var/log/netbird/client.log" StandardOutput=file:/var/log/netbird/netbird.out StandardError=file:/var/log/netbird/netbird.err Restart=always RestartSec=120 EnvironmentFile=-/etc/sysconfig/netbird [Install] WantedBy=multi-user.target And then I checked out the json file `/etc/netbird/config.json`: { "PrivateKey": "somesortofprivatekey", "PreSharedKey": "", "ManagementURL": { "Scheme": "https", "Opaque": "", "User": null, "Host": "[api.wiretrustee.com:443](http://api.wiretrustee.com:443/)", "Path": "", "RawPath": "", "OmitHost": false, "ForceQuery": false, "RawQuery": "", "Fragment": "", "RawFragment": "" }, "AdminURL": { "Scheme": "https", "Opaque": "", "User": null, "Host": "[app.netbird.io:443](http://app.netbird.io:443/)", "Path": "", "RawPath": "", "OmitHost": false, "ForceQuery": false, "RawQuery": "", "Fragment": "", "RawFragment": "" }, "WgIface": "wt0", "WgPort": 51820, "IFaceBlackList": [ "wt0", "wt", "utun", "tun0", "zt", "ZeroTier", "wg", "ts", "Tailscale", "tailscale", "docker", "veth", "br-" ], "DisableIPv6Discovery": false, "SSHKey": "-----BEGIN PRIVATE KEY-----\nsomesortofsshprivatekeydata\n-----END PRIVATE KEY-----\n", "NATExternalIPs": null, "CustomDNSAddress": "" } Changing everything to user level will indeed not work. Apparently the socket is created and used (mandatory), which does not seem to be configurable. As the OP states, it would be great if this could be enhanced.
Author
Owner

@hg commented on GitHub (Apr 6, 2023):

I used to patch that, but running under a restricted user will not work these days for other reasons (I don't remember all of the issues I've faced, but one of them was probably intraction with systemd-resolved or rewriting /etc/resolv.conf). If you don't need automatic DNS it should still work — apply the patch and rebuild.

@hg commented on GitHub (Apr 6, 2023): I used to [patch that](https://aur.archlinux.org/cgit/aur.git/tree/0001-fix-run-paths.patch?h=netbird&id=914ad7ecac64fe2f4d1fa311b524f94cbb621716), but running under a restricted user will not work these days for other reasons (I don't remember all of the issues I've faced, but one of them was probably intraction with `systemd-resolved` or rewriting `/etc/resolv.conf`). If you don't need automatic DNS it should still work — apply the patch and rebuild.
Author
Owner

@CertainLach commented on GitHub (Dec 30, 2023):

For automatic dns with systemd-resolved, this polkit rule needs to be added:

      polkit.addRule(function(action, subject) {
        if((action.id == "org.freedesktop.resolve1.set-dns-servers" ||
            action.id == "org.freedesktop.resolve1.set-domains") &&
           subject.user == "netbird") {
          return polkit.Result.YES;
        }
      })

Bpf program should also be rebuilt, as it uses bpf_trace_printk, which is privileged, because it might slow down the OS.

@CertainLach commented on GitHub (Dec 30, 2023): For automatic dns with systemd-resolved, this polkit rule needs to be added: ```js polkit.addRule(function(action, subject) { if((action.id == "org.freedesktop.resolve1.set-dns-servers" || action.id == "org.freedesktop.resolve1.set-domains") && subject.user == "netbird") { return polkit.Result.YES; } }) ``` Bpf program should also be rebuilt, as it uses bpf_trace_printk, which is privileged, because it might slow down the OS.
Author
Owner

@nazarewk commented on GitHub (Apr 17, 2025):

Not looking much into the discussion now, but seems the NixOS module hardening is very relevant to the topic.

@nazarewk commented on GitHub (Apr 17, 2025): Not looking much into the discussion now, but seems [the NixOS module hardening](https://github.com/NixOS/nixpkgs/blob/2631b0b7abcea6e640ce31cd78ea58910d31e650/nixos/modules/services/networking/netbird.nix#L531-L603) is very relevant to the topic.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: SVI/netbird#146