at 23.11-pre 5.5 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 (mdDoc "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 = mdDoc '' 38 Declarative configuration of firewall rules. 39 All rules will be stored in `/var/lib/opensnitch/rules`. 40 See [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Rules) 41 for available options. 42 ''; 43 44 type = types.submodule { 45 freeformType = format.type; 46 }; 47 }; 48 49 settings = mkOption { 50 type = types.submodule { 51 freeformType = format.type; 52 53 options = { 54 Server = { 55 56 Address = mkOption { 57 type = types.str; 58 description = mdDoc '' 59 Unix socket path (unix:///tmp/osui.sock, the "unix:///" part is 60 mandatory) or TCP socket (192.168.1.100:50051). 61 ''; 62 }; 63 64 LogFile = mkOption { 65 type = types.path; 66 description = mdDoc '' 67 File to write logs to (use /dev/stdout to write logs to standard 68 output). 69 ''; 70 }; 71 72 }; 73 74 DefaultAction = mkOption { 75 type = types.enum [ "allow" "deny" ]; 76 description = mdDoc '' 77 Default action whether to block or allow application internet 78 access. 79 ''; 80 }; 81 82 DefaultDuration = mkOption { 83 type = types.enum [ 84 "once" "always" "until restart" "30s" "5m" "15m" "30m" "1h" 85 ]; 86 description = mdDoc '' 87 Default duration of firewall rule. 88 ''; 89 }; 90 91 InterceptUnknown = mkOption { 92 type = types.bool; 93 description = mdDoc '' 94 Whether to intercept spare connections. 95 ''; 96 }; 97 98 ProcMonitorMethod = mkOption { 99 type = types.enum [ "ebpf" "proc" "ftrace" "audit" ]; 100 description = mdDoc '' 101 Which process monitoring method to use. 102 ''; 103 }; 104 105 LogLevel = mkOption { 106 type = types.enum [ 0 1 2 3 4 ]; 107 description = mdDoc '' 108 Default log level from 0 to 4 (debug, info, important, warning, 109 error). 110 ''; 111 }; 112 113 Firewall = mkOption { 114 type = types.enum [ "iptables" "nftables" ]; 115 description = mdDoc '' 116 Which firewall backend to use. 117 ''; 118 }; 119 120 Stats = { 121 122 MaxEvents = mkOption { 123 type = types.int; 124 description = mdDoc '' 125 Max events to send to the GUI. 126 ''; 127 }; 128 129 MaxStats = mkOption { 130 type = types.int; 131 description = mdDoc '' 132 Max stats per item to keep in backlog. 133 ''; 134 }; 135 136 }; 137 }; 138 }; 139 description = mdDoc '' 140 opensnitchd configuration. Refer to [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Configurations) 141 for details on supported values. 142 ''; 143 }; 144 }; 145 }; 146 147 config = mkIf cfg.enable { 148 149 # pkg.opensnitch is referred to elsewhere in the module so we don't need to worry about it being garbage collected 150 services.opensnitch.settings = mapAttrs (_: v: mkDefault v) (builtins.fromJSON (builtins.unsafeDiscardStringContext (builtins.readFile "${pkgs.opensnitch}/etc/default-config.json"))); 151 152 systemd = { 153 packages = [ pkgs.opensnitch ]; 154 services.opensnitchd.wantedBy = [ "multi-user.target" ]; 155 }; 156 157 systemd.services.opensnitchd.preStart = mkIf (cfg.rules != {}) (let 158 rules = flip mapAttrsToList predefinedRules (file: content: { 159 inherit (content) file; 160 local = "/var/lib/opensnitch/rules/${file}.json"; 161 }); 162 in '' 163 # Remove all firewall rules from `/var/lib/opensnitch/rules` that are symlinks to a store-path, 164 # but aren't declared in `cfg.rules` (i.e. all networks that were "removed" from 165 # `cfg.rules`). 166 find /var/lib/opensnitch/rules -type l -lname '${builtins.storeDir}/*' ${optionalString (rules != {}) '' 167 -not \( ${concatMapStringsSep " -o " ({ local, ... }: 168 "-name '${baseNameOf local}*'") 169 rules} \) \ 170 ''} -delete 171 ${concatMapStrings ({ file, local }: '' 172 ln -sf '${file}' "${local}" 173 '') rules} 174 ''); 175 176 environment.etc."opensnitchd/default-config.json".source = format.generate "default-config.json" cfg.settings; 177 178 }; 179} 180