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