I am trying to setup the OpenSnitch (application firewall) on my NixOS machine. I am running NixOS unstable using a flake based setup.
I have OpenSnitch installed and setup in my opensnitch.nix config file and it works and I can manually save rules using the GUI when applications try to access the network. However all the rules get reset when I upgrade my system because the rules use the application path in the nix store which changes on upgrade.
So I have just spent several hours defining rules in my opensnitch.nix file using services.opensnitch.rules as in there you can use ${lib.getBin pkgs.PKG_NAME} to dynamically get the nix path in the store for a given executable and it will auto update on system upgrade.
The rules created manually with the GUI live in the /var/lib/opensnitch/rules directory as a .json file per rule. Each rule defined in opensnitch.nix is added as a symlink in this directory pointing to the relevant nix store path. The nix defined rules show up in the GUI but they do not work, as I still get asked manually for approval for connections that the rule allows.
I have compared the .json file of the same rule created with the GUI against the version of the same rule I have defined in opensnitch.nix and they have the same content but the nix defined one is not nicely formatted json. I have tried everything I can think of so I am coming here to look for some help to figure this out!
Does anyone have experience with writing opensnitch rules or tips for next steps to figure this out?
I have now worked out how to define rules successfully in configuration.nix. The rules are very long but they do work:
Example of rule to allow firefox to talk on port 443
"000-firefox-allow-port-443" = {
name = "Firefox-allow-port-443";
enabled = true;
action = "allow";
duration = "always";
operator = {
type = "list";
operand = "list";
data = "[{\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"443\"}, {\"type\": \"simple\",\"operand\": \"user.id\", \"data\": \"1000\"}, {\"type\": \"simple\", \"operand\": \"process.path\", \"data\": \"${lib.getBin pkgs.firefox}/lib/firefox/firefox\"}]";
list = [
{
operand = "dest.port";
data = "443";
type = "simple";
list = null;
sensitive = false;
}
{
operand = "user.id";
data = "1000";
type = "simple";
list = null;
sensitive = false;
}
{
operand = "process.path";
data = "${lib.getBin pkgs.firefox}/lib/firefox/firefox";
type = "simple";
list = null;
sensitive = false;
}
];
};
};
However sometimes they do not work and an opensnitch dialog asking about a connection that is already allowed in the defined rules in nix land is shown. I have not yet worked out why this is and if there is any rhyme or reason to it. systemd-resolved is the main culprit!
Yes exactly, it seems silly to repeat it all but I found that was the only way to get the rules to work as expected.
I think I tested the data value not being on one line with strings like that but can’t remember for sure. But how I have defined it above does work for me.
I think it would be a great addition for the opensnitch NixOS module to write a rule converter so we can write simple short rules in our .nix config files that then get turned into rules opensnitch is happy with
@rhendric your function works perfectly to allow a bunch of rules. However, it is hardcoded to only allow. I tried to reason about how to optionally deny, but I am likely on my way to completely rewriting it.
Can you (or anyone else) see an easy way to modify the function to somehow also specify additional attrs to override things like this?