at 25.11-pre 7.6 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.opensnitch; 9 format = pkgs.formats.json { }; 10 11 predefinedRules = lib.flip lib.mapAttrs cfg.rules ( 12 name: cfg: { 13 file = pkgs.writeText "rule" (builtins.toJSON cfg); 14 } 15 ); 16 17in 18{ 19 options = { 20 services.opensnitch = { 21 enable = lib.mkEnableOption "Opensnitch application firewall"; 22 23 rules = lib.mkOption { 24 default = { }; 25 example = lib.literalExpression '' 26 { 27 "tor" = { 28 "name" = "tor"; 29 "enabled" = true; 30 "action" = "allow"; 31 "duration" = "always"; 32 "operator" = { 33 "type" ="simple"; 34 "sensitive" = false; 35 "operand" = "process.path"; 36 "data" = "''${lib.getBin pkgs.tor}/bin/tor"; 37 }; 38 }; 39 }; 40 ''; 41 42 description = '' 43 Declarative configuration of firewall rules. 44 All rules will be stored in `/var/lib/opensnitch/rules` by default. 45 Rules path can be configured with `settings.Rules.Path`. 46 See [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Rules) 47 for available options. 48 ''; 49 50 type = lib.types.submodule { 51 freeformType = format.type; 52 }; 53 }; 54 55 settings = lib.mkOption { 56 type = lib.types.submodule { 57 freeformType = format.type; 58 59 options = { 60 Server = { 61 62 Address = lib.mkOption { 63 type = lib.types.str; 64 description = '' 65 Unix socket path (unix:///tmp/osui.sock, the "unix:///" part is 66 mandatory) or TCP socket (192.168.1.100:50051). 67 ''; 68 }; 69 70 LogFile = lib.mkOption { 71 type = lib.types.path; 72 description = '' 73 File to write logs to (use /dev/stdout to write logs to standard 74 output). 75 ''; 76 }; 77 78 }; 79 80 DefaultAction = lib.mkOption { 81 type = lib.types.enum [ 82 "allow" 83 "deny" 84 ]; 85 description = '' 86 Default action whether to block or allow application internet 87 access. 88 ''; 89 }; 90 91 InterceptUnknown = lib.mkOption { 92 type = lib.types.bool; 93 description = '' 94 Whether to intercept spare connections. 95 ''; 96 }; 97 98 ProcMonitorMethod = lib.mkOption { 99 type = lib.types.enum [ 100 "ebpf" 101 "proc" 102 "ftrace" 103 "audit" 104 ]; 105 description = '' 106 Which process monitoring method to use. 107 ''; 108 }; 109 110 LogLevel = lib.mkOption { 111 type = lib.types.enum [ 112 0 113 1 114 2 115 3 116 4 117 ]; 118 description = '' 119 Default log level from 0 to 4 (debug, info, important, warning, 120 error). 121 ''; 122 }; 123 124 Firewall = lib.mkOption { 125 type = lib.types.enum [ 126 "iptables" 127 "nftables" 128 ]; 129 description = '' 130 Which firewall backend to use. 131 ''; 132 }; 133 134 Stats = { 135 136 MaxEvents = lib.mkOption { 137 type = lib.types.int; 138 description = '' 139 Max events to send to the GUI. 140 ''; 141 }; 142 143 MaxStats = lib.mkOption { 144 type = lib.types.int; 145 description = '' 146 Max stats per item to keep in backlog. 147 ''; 148 }; 149 150 }; 151 152 Ebpf.ModulesPath = lib.mkOption { 153 type = lib.types.nullOr lib.types.path; 154 default = 155 if cfg.settings.ProcMonitorMethod == "ebpf" then 156 "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd" 157 else 158 null; 159 defaultText = lib.literalExpression '' 160 if cfg.settings.ProcMonitorMethod == "ebpf" then 161 "\\$\\{config.boot.kernelPackages.opensnitch-ebpf\\}/etc/opensnitchd" 162 else null; 163 ''; 164 description = '' 165 Configure eBPF modules path. Used when 166 `settings.ProcMonitorMethod` is set to `ebpf`. 167 ''; 168 }; 169 170 Rules.Path = lib.mkOption { 171 type = lib.types.path; 172 default = "/var/lib/opensnitch/rules"; 173 description = '' 174 Path to the directory where firewall rules can be found and will 175 get stored by the NixOS module. 176 ''; 177 }; 178 179 }; 180 }; 181 description = '' 182 opensnitchd configuration. Refer to [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Configurations) 183 for details on supported values. 184 ''; 185 }; 186 }; 187 }; 188 189 config = lib.mkIf cfg.enable { 190 191 # pkg.opensnitch is referred to elsewhere in the module so we don't need to worry about it being garbage collected 192 services.opensnitch.settings = lib.mapAttrs (_: v: lib.mkDefault v) ( 193 builtins.fromJSON ( 194 builtins.unsafeDiscardStringContext ( 195 builtins.readFile "${pkgs.opensnitch}/etc/opensnitchd/default-config.json" 196 ) 197 ) 198 ); 199 200 systemd = { 201 packages = [ pkgs.opensnitch ]; 202 services.opensnitchd = { 203 wantedBy = [ "multi-user.target" ]; 204 serviceConfig = { 205 ExecStart = 206 let 207 preparedSettings = removeAttrs cfg.settings ( 208 lib.optional (cfg.settings.ProcMonitorMethod != "ebpf") "Ebpf" 209 ); 210 in 211 [ 212 "" 213 "${pkgs.opensnitch}/bin/opensnitchd --config-file ${format.generate "default-config.json" preparedSettings}" 214 ]; 215 }; 216 preStart = lib.mkIf (cfg.rules != { }) ( 217 let 218 rules = lib.flip lib.mapAttrsToList predefinedRules ( 219 file: content: { 220 inherit (content) file; 221 local = "${cfg.settings.Rules.Path}/${file}.json"; 222 } 223 ); 224 in 225 '' 226 # Remove all firewall rules from rules path (configured with 227 # cfg.settings.Rules.Path) that are symlinks to a store-path, but aren't 228 # declared in `cfg.rules` (i.e. all networks that were "removed" from 229 # `cfg.rules`). 230 find ${cfg.settings.Rules.Path} -type l -lname '${builtins.storeDir}/*' ${ 231 lib.optionalString (rules != { }) '' 232 -not \( ${ 233 lib.concatMapStringsSep " -o " ({ local, ... }: "-name '${baseNameOf local}*'") rules 234 } \) \ 235 '' 236 } -delete 237 ${lib.concatMapStrings ( 238 { file, local }: 239 '' 240 ln -sf '${file}' "${local}" 241 '' 242 ) rules} 243 '' 244 ); 245 }; 246 tmpfiles.rules = [ 247 "d ${cfg.settings.Rules.Path} 0750 root root - -" 248 "L+ /etc/opensnitchd/system-fw.json - - - - ${pkgs.opensnitch}/etc/opensnitchd/system-fw.json" 249 ]; 250 }; 251 252 }; 253 254 meta.maintainers = with lib.maintainers; [ onny ]; 255}