lol

nixos/usbguard: create package and module (#28363)

* nixos/usbguard: create package and module

No usbguard module or package existed for NixOS previously. USBGuard
will protect you from BadUSB attacks. (assuming configuration is done
correctly)

* nixos/usbguard: remove extra packages

Users can override this by themselves.

* nixos/usbguard: add maintainer and fix style

authored by

Phil and committed by
Jörg Thalheim
4f293539 5aefcd22

+319
+1
lib/maintainers.nix
··· 578 578 thoughtpolice = "Austin Seipp <aseipp@pobox.com>"; 579 579 timbertson = "Tim Cuthbertson <tim@gfxmonk.net>"; 580 580 titanous = "Jonathan Rudenberg <jonathan@titanous.com>"; 581 + tnias = "Philipp Bartsch <phil@grmr.de>"; 581 582 tohl = "Tomas Hlavaty <tom@logand.com>"; 582 583 tokudan = "Daniel Frank <git@danielfrank.net>"; 583 584 tomberek = "Thomas Bereknyei <tomberek@gmail.com>";
+1
nixos/modules/module-list.nix
··· 563 563 ./services/security/tor.nix 564 564 ./services/security/torify.nix 565 565 ./services/security/torsocks.nix 566 + ./services/security/usbguard.nix 566 567 ./services/security/vault.nix 567 568 ./services/system/cgmanager.nix 568 569 ./services/system/cloud-init.nix
+200
nixos/modules/services/security/usbguard.nix
··· 1 + {config, lib, pkgs, ... }: 2 + 3 + with lib; 4 + 5 + let 6 + 7 + cfg = config.services.usbguard; 8 + 9 + # valid policy options 10 + policy = (types.enum [ "allow" "block" "reject" "keep" "apply-policy" ]); 11 + 12 + # decide what file to use for rules 13 + ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else cfg.ruleFile; 14 + 15 + daemonConf = '' 16 + # generated by nixos/modules/services/security/usbguard.nix 17 + RuleFile=${ruleFile} 18 + ImplicitPolicyTarget=${cfg.implictPolicyTarget} 19 + PresentDevicePolicy=${cfg.presentDevicePolicy} 20 + PresentControllerPolicy=${cfg.presentControllerPolicy} 21 + InsertedDevicePolicy=${cfg.insertedDevicePolicy} 22 + RestoreControllerDeviceState=${if cfg.restoreControllerDeviceState then "true" else "false"} 23 + # this does not seem useful for endusers to change 24 + DeviceManagerBackend=uevent 25 + IPCAllowedUsers=${concatStringsSep " " cfg.IPCAllowedUsers} 26 + IPCAllowedGroups=${concatStringsSep " " cfg.IPCAllowedGroups} 27 + IPCAccessControlFiles=${cfg.IPCAccessControlFiles} 28 + DeviceRulesWithPort=${if cfg.deviceRulesWithPort then "true" else "false"} 29 + AuditFilePath=${cfg.auditFilePath} 30 + ''; 31 + 32 + daemonConfFile = pkgs.writeText "usbguard-daemon-conf" daemonConf; 33 + 34 + in { 35 + 36 + ###### interface 37 + 38 + options = { 39 + services.usbguard = { 40 + enable = mkEnableOption "USBGuard daemon"; 41 + 42 + ruleFile = mkOption { 43 + type = types.path; 44 + default = "/var/lib/usbguard/rules.conf"; 45 + description = '' 46 + The USBGuard daemon will use this file to load the policy rule set 47 + from it and to write new rules received via the IPC interface. 48 + 49 + Running the command <literal>usbguard generate-policy</literal> as 50 + root will generate a config for your currently plugged in devices. 51 + For a in depth guide consult the official documentation. 52 + 53 + Setting the <literal>rules</literal> option will ignore the 54 + <literal>ruleFile</literal> option. 55 + ''; 56 + }; 57 + 58 + rules = mkOption { 59 + type = types.nullOr types.str; 60 + default = null; 61 + example = '' 62 + allow with-interface equals { 08:*:* } 63 + ''; 64 + description = '' 65 + The USBGuard daemon will load this policy rule set. Modifying it via 66 + the IPC interface won't work if you use this option, since the 67 + contents of this option will be written into the nix-store it will be 68 + read-only. 69 + 70 + You can still use <literal> usbguard generate-policy</literal> to 71 + generate rules, but you would have to insert them here. 72 + 73 + Setting the <literal>rules</literal> option will ignore the 74 + <literal>ruleFile</literal> option. 75 + ''; 76 + }; 77 + 78 + implictPolicyTarget = mkOption { 79 + type = policy; 80 + default = "block"; 81 + description = '' 82 + How to treat USB devices that don't match any rule in the policy. 83 + Target should be one of allow, block or reject (logically remove the 84 + device node from the system). 85 + ''; 86 + }; 87 + 88 + presentDevicePolicy = mkOption { 89 + type = policy; 90 + default = "apply-policy"; 91 + description = '' 92 + How to treat USB devices that are already connected when the daemon 93 + starts. Policy should be one of allow, block, reject, keep (keep 94 + whatever state the device is currently in) or apply-policy (evaluate 95 + the rule set for every present device). 96 + ''; 97 + }; 98 + 99 + presentControllerPolicy = mkOption { 100 + type = policy; 101 + default = "keep"; 102 + description = '' 103 + How to treat USB controller devices that are already connected when 104 + the daemon starts. One of allow, block, reject, keep or apply-policy. 105 + ''; 106 + }; 107 + 108 + insertedDevicePolicy = mkOption { 109 + type = policy; 110 + default = "apply-policy"; 111 + description = '' 112 + How to treat USB devices that are already connected after the daemon 113 + starts. One of block, reject, apply-policy. 114 + ''; 115 + }; 116 + 117 + restoreControllerDeviceState = mkOption { 118 + type = types.bool; 119 + default = false; 120 + description = '' 121 + The USBGuard daemon modifies some attributes of controller 122 + devices like the default authorization state of new child device 123 + instances. Using this setting, you can controll whether the daemon 124 + will try to restore the attribute values to the state before 125 + modificaton on shutdown. 126 + ''; 127 + }; 128 + 129 + IPCAllowedUsers = mkOption { 130 + type = types.listOf types.str; 131 + default = [ "root" ]; 132 + example = [ "root" "yourusername" ]; 133 + description = '' 134 + A list of usernames that the daemon will accept IPC connections from. 135 + ''; 136 + }; 137 + 138 + IPCAllowedGroups = mkOption { 139 + type = types.listOf types.str; 140 + default = [ ]; 141 + example = [ "wheel" ]; 142 + description = '' 143 + A list of groupnames that the daemon will accept IPC connections 144 + from. 145 + ''; 146 + }; 147 + 148 + IPCAccessControlFiles = mkOption { 149 + type = types.path; 150 + default = "/var/lib/usbguard/IPCAccessControl.d/"; 151 + description = '' 152 + The files at this location will be interpreted by the daemon as IPC 153 + access control definition files. See the IPC ACCESS CONTROL section 154 + in <citerefentry><refentrytitle>usbguard-daemon.conf</refentrytitle> 155 + <manvolnum>5</manvolnum></citerefentry> for more details. 156 + ''; 157 + }; 158 + 159 + deviceRulesWithPort = mkOption { 160 + type = types.bool; 161 + default = false; 162 + description = '' 163 + Generate device specific rules including the "via-port" attribute. 164 + ''; 165 + }; 166 + 167 + auditFilePath = mkOption { 168 + type = types.path; 169 + default = "/var/log/usbguard/usbguard-audit.log"; 170 + description = '' 171 + USBGuard audit events log file path. 172 + ''; 173 + }; 174 + }; 175 + }; 176 + 177 + 178 + ###### implementation 179 + 180 + config = mkIf cfg.enable { 181 + 182 + environment.systemPackages = [ pkgs.usbguard ]; 183 + 184 + systemd.services.usbguard = { 185 + description = "USBGuard daemon"; 186 + 187 + wantedBy = [ "basic.target" ]; 188 + wants = [ "systemd-udevd.service" "local-fs.target" ]; 189 + 190 + # make sure an empty rule file and required directories exist 191 + preStart = ''mkdir -p $(dirname "${cfg.ruleFile}") "${cfg.IPCAccessControlFiles}" && ([ -f "${cfg.ruleFile}" ] || touch ${cfg.ruleFile})''; 192 + 193 + serviceConfig = { 194 + Type = "simple"; 195 + ExecStart = ''${pkgs.usbguard}/bin/usbguard-daemon -d -k -c ${daemonConfFile}''; 196 + Restart = "on-failure"; 197 + }; 198 + }; 199 + }; 200 + }
+13
pkgs/os-specific/linux/usbguard/daemon_read_only_config.patch
··· 1 + diff --git a/src/Library/ConfigFilePrivate.cpp b/src/Library/ConfigFilePrivate.cpp 2 + index 8aefa65..40914f7 100644 3 + --- a/src/Library/ConfigFilePrivate.cpp 4 + +++ b/src/Library/ConfigFilePrivate.cpp 5 + @@ -51,7 +51,7 @@ namespace usbguard 6 + 7 + void ConfigFilePrivate::open(const std::string& path) 8 + { 9 + - _stream.open(path, std::ios::in|std::ios::out); 10 + + _stream.open(path, std::ios::in); 11 + if (!_stream.is_open()) { 12 + throw std::runtime_error("Can't open " + path); 13 + }
+68
pkgs/os-specific/linux/usbguard/default.nix
··· 1 + { 2 + stdenv, fetchurl, lib, 3 + libxslt, pandoc, pkgconfig, 4 + dbus_glib, libcap_ng, libqb, libseccomp, polkit, protobuf, qtbase, qttools, qtsvg, 5 + libgcrypt ? null, 6 + libsodium ? null 7 + }: 8 + 9 + with stdenv.lib; 10 + 11 + assert libgcrypt != null -> libsodium == null; 12 + 13 + stdenv.mkDerivation rec { 14 + version = "0.7.0"; 15 + name = "usbguard-${version}"; 16 + 17 + repo = "https://github.com/dkopecek/usbguard"; 18 + 19 + src = fetchurl { 20 + url = "${repo}/releases/download/${name}/${name}.tar.gz"; 21 + sha256 = "1e1485a2b47ba3bde9de2851b371d2552a807047a21e0b81553cf80d7f722709"; 22 + }; 23 + 24 + patches = [ 25 + ./daemon_read_only_config.patch 26 + ./documentation.patch 27 + ]; 28 + 29 + nativeBuildInputs = [ 30 + libxslt 31 + pandoc # for rendering documentation 32 + pkgconfig 33 + ]; 34 + 35 + buildInputs = [ 36 + dbus_glib 37 + libcap_ng 38 + libqb 39 + libseccomp 40 + polkit 41 + protobuf 42 + 43 + qtbase 44 + qtsvg 45 + qttools 46 + ] 47 + ++ (lib.optional (libgcrypt != null) libgcrypt) 48 + ++ (lib.optional (libsodium != null) libsodium); 49 + 50 + configureFlags = [ 51 + "--with-bundled-catch" 52 + "--with-bundled-pegtl" 53 + "--with-dbus" 54 + "--with-gui-qt=qt5" 55 + "--with-polkit" 56 + ] 57 + ++ (lib.optional (libgcrypt != null) "--with-crypto-library=gcrypt") 58 + ++ (lib.optional (libsodium != null) "--with-crypto-library=sodium"); 59 + 60 + enableParallelBuilding = true; 61 + 62 + meta = { 63 + description = "The USBGuard software framework helps to protect your computer against BadUSB."; 64 + homepage = "https://dkopecek.github.io/usbguard/"; 65 + license = licenses.gpl2; 66 + maintainers = [ maintainers.tnias ]; 67 + }; 68 + }
+32
pkgs/os-specific/linux/usbguard/documentation.patch
··· 1 + diff --git a/doc/usbguard-daemon.conf.5.md b/doc/usbguard-daemon.conf.5.md 2 + index ea86ad1..63aec70 100644 3 + --- a/doc/usbguard-daemon.conf.5.md 4 + +++ b/doc/usbguard-daemon.conf.5.md 5 + @@ -30,21 +30,21 @@ The **usbguard-daemon.conf** file is loaded by the USBGuard daemon after it pars 6 + **RestoreControllerDeviceState**=<*boolean*> 7 + : The USBGuard daemon modifies some attributes of controller devices like the default authorization state of new child device instances. Using this setting, you can control whether the daemon will try to restore the attribute values to the state before modification on shutdown. 8 + 9 + +**DeviceManagerBackend**=<*backend*> 10 + +: Which device manager backend implementation to use. Backend should be one of `uevent` (default) or `dummy`. 11 + + 12 + **IPCAllowedUsers**=<*username*> [<*username*> ...] 13 + : A space delimited list of usernames that the daemon will accept IPC connections from. 14 + 15 + **IPCAllowedGroups**=<*groupname*> [<*groupname*> ...] 16 + : A space delimited list of groupnames that the daemon will accept IPC connections from. 17 + 18 + -**IPCAccessControlFiles**=<*path*> 19 + -: Path to a directory holding the IPC access control files. 20 + - 21 + -**DeviceManagerBackend**=<*backend*> 22 + -: Which device manager backend implementation to use. Backend should be one of `uevent` (default) or `dummy`. 23 + - 24 + **IPCAccessControlFiles**=<*path*> 25 + : The files at this location will be interpreted by the daemon as IPC access control definition files. See the **IPC ACCESS CONTROL** section for more details. 26 + 27 + +**DeviceRulesWithPort**=<*boolean*> 28 + +: Generate device specific rules including the "via-port" attribute. 29 + + 30 + **AuditFilePath**=<*filepath*> 31 + : USBGuard audit events log file path. 32 +
+4
pkgs/top-level/all-packages.nix
··· 12726 12726 12727 12727 upstart-check-config = callPackage ../os-specific/linux/upstart/check-config.nix {}; 12728 12728 12729 + usbguard = libsForQt5.callPackage ../os-specific/linux/usbguard { 12730 + libgcrypt = null; 12731 + }; 12732 + 12729 12733 usbutils = callPackage ../os-specific/linux/usbutils { }; 12730 12734 12731 12735 usermount = callPackage ../os-specific/linux/usermount { };