294 lines
8.9 KiB
Nix
294 lines
8.9 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
with lib;
|
|
let
|
|
cfg = config.services.usbguard;
|
|
|
|
# valid policy options
|
|
policy = (
|
|
types.enum [
|
|
"allow"
|
|
"block"
|
|
"reject"
|
|
"keep"
|
|
"apply-policy"
|
|
]
|
|
);
|
|
|
|
# decide what file to use for rules
|
|
ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else cfg.ruleFile;
|
|
|
|
daemonConf = ''
|
|
# generated by nixos/modules/services/security/usbguard.nix
|
|
RuleFile=${ruleFile}
|
|
ImplicitPolicyTarget=${cfg.implicitPolicyTarget}
|
|
PresentDevicePolicy=${cfg.presentDevicePolicy}
|
|
PresentControllerPolicy=${cfg.presentControllerPolicy}
|
|
InsertedDevicePolicy=${cfg.insertedDevicePolicy}
|
|
RestoreControllerDeviceState=${boolToString cfg.restoreControllerDeviceState}
|
|
# this does not seem useful for endusers to change
|
|
DeviceManagerBackend=uevent
|
|
IPCAllowedUsers=${concatStringsSep " " cfg.IPCAllowedUsers}
|
|
IPCAllowedGroups=${concatStringsSep " " cfg.IPCAllowedGroups}
|
|
IPCAccessControlFiles=/var/lib/usbguard/IPCAccessControl.d/
|
|
DeviceRulesWithPort=${boolToString cfg.deviceRulesWithPort}
|
|
# HACK: that way audit logs still land in the journal
|
|
AuditFilePath=/dev/null
|
|
'';
|
|
|
|
daemonConfFile = pkgs.writeText "usbguard-daemon-conf" daemonConf;
|
|
|
|
in
|
|
{
|
|
|
|
###### interface
|
|
|
|
options = {
|
|
services.usbguard = {
|
|
enable = mkEnableOption "USBGuard daemon";
|
|
|
|
package = mkPackageOption pkgs "usbguard" {
|
|
extraDescription = ''
|
|
If you do not need the Qt GUI, use `pkgs.usbguard-nox` to save disk space.
|
|
'';
|
|
};
|
|
|
|
ruleFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
default = "/var/lib/usbguard/rules.conf";
|
|
example = "/run/secrets/usbguard-rules";
|
|
description = ''
|
|
This tells the USBGuard daemon which file to load as policy rule set.
|
|
|
|
The file can be changed manually or via the IPC interface assuming it has the right file permissions.
|
|
|
|
For more details see {manpage}`usbguard-rules.conf(5)`.
|
|
'';
|
|
|
|
};
|
|
rules = mkOption {
|
|
type = types.nullOr types.lines;
|
|
default = null;
|
|
example = ''
|
|
allow with-interface equals { 08:*:* }
|
|
'';
|
|
description = ''
|
|
The USBGuard daemon will load this as the policy rule set.
|
|
As these rules are NixOS managed they are immutable and can't
|
|
be changed by the IPC interface.
|
|
|
|
If you do not set this option, the USBGuard daemon will load
|
|
it's policy rule set from the option configured in `services.usbguard.ruleFile`.
|
|
|
|
Running `usbguard generate-policy` as root will
|
|
generate a config for your currently plugged in devices.
|
|
|
|
For more details see {manpage}`usbguard-rules.conf(5)`.
|
|
'';
|
|
};
|
|
|
|
implicitPolicyTarget = mkOption {
|
|
type = types.enum [
|
|
"allow"
|
|
"block"
|
|
"reject"
|
|
];
|
|
default = "block";
|
|
description = ''
|
|
How to treat USB devices that don't match any rule in the policy.
|
|
Target should be one of allow, block or reject (logically remove the
|
|
device node from the system).
|
|
'';
|
|
};
|
|
|
|
presentDevicePolicy = mkOption {
|
|
type = policy;
|
|
default = "apply-policy";
|
|
description = ''
|
|
How to treat USB devices that are already connected when the daemon
|
|
starts. Policy should be one of allow, block, reject, keep (keep
|
|
whatever state the device is currently in) or apply-policy (evaluate
|
|
the rule set for every present device).
|
|
'';
|
|
};
|
|
|
|
presentControllerPolicy = mkOption {
|
|
type = policy;
|
|
default = "keep";
|
|
description = ''
|
|
How to treat USB controller devices that are already connected when
|
|
the daemon starts. One of allow, block, reject, keep or apply-policy.
|
|
'';
|
|
};
|
|
|
|
insertedDevicePolicy = mkOption {
|
|
type = types.enum [
|
|
"block"
|
|
"reject"
|
|
"apply-policy"
|
|
];
|
|
default = "apply-policy";
|
|
description = ''
|
|
How to treat USB devices that are already connected after the daemon
|
|
starts. One of block, reject, apply-policy.
|
|
'';
|
|
};
|
|
|
|
restoreControllerDeviceState = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
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.
|
|
'';
|
|
};
|
|
|
|
IPCAllowedUsers = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ "root" ];
|
|
example = [
|
|
"root"
|
|
"yourusername"
|
|
];
|
|
description = ''
|
|
A list of usernames that the daemon will accept IPC connections from.
|
|
'';
|
|
};
|
|
|
|
IPCAllowedGroups = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ ];
|
|
example = [ "wheel" ];
|
|
description = ''
|
|
A list of groupnames that the daemon will accept IPC connections
|
|
from.
|
|
'';
|
|
};
|
|
|
|
deviceRulesWithPort = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
Generate device specific rules including the "via-port" attribute.
|
|
'';
|
|
};
|
|
|
|
dbus.enable = mkEnableOption "USBGuard dbus daemon";
|
|
};
|
|
};
|
|
|
|
###### implementation
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
environment.systemPackages = [ cfg.package ];
|
|
|
|
systemd.services = {
|
|
usbguard = {
|
|
description = "USBGuard daemon";
|
|
|
|
wantedBy = [ "basic.target" ];
|
|
wants = [ "systemd-udevd.service" ];
|
|
|
|
# make sure an empty rule file exists
|
|
preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}'';
|
|
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}";
|
|
Restart = "on-failure";
|
|
|
|
StateDirectory = [
|
|
"usbguard"
|
|
"usbguard/IPCAccessControl.d"
|
|
];
|
|
|
|
AmbientCapabilities = "";
|
|
CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER";
|
|
DeviceAllow = "/dev/null rw";
|
|
DevicePolicy = "strict";
|
|
IPAddressDeny = "any";
|
|
LockPersonality = true;
|
|
MemoryDenyWriteExecute = true;
|
|
NoNewPrivileges = true;
|
|
PrivateDevices = true;
|
|
PrivateTmp = true;
|
|
ProtectControlGroups = true;
|
|
ProtectHome = true;
|
|
ProtectKernelModules = true;
|
|
ProtectSystem = true;
|
|
ReadOnlyPaths = "-/";
|
|
ReadWritePaths = "-/dev/shm -/tmp";
|
|
RestrictAddressFamilies = [
|
|
"AF_UNIX"
|
|
"AF_NETLINK"
|
|
];
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
SystemCallArchitectures = "native";
|
|
SystemCallFilter = "@system-service";
|
|
UMask = "0077";
|
|
};
|
|
};
|
|
|
|
usbguard-dbus = mkIf cfg.dbus.enable {
|
|
description = "USBGuard D-Bus Service";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
requires = [ "usbguard.service" ];
|
|
|
|
serviceConfig = {
|
|
Type = "dbus";
|
|
BusName = "org.usbguard1";
|
|
ExecStart = "${cfg.package}/bin/usbguard-dbus --system";
|
|
Restart = "on-failure";
|
|
};
|
|
|
|
aliases = [ "dbus-org.usbguard.service" ];
|
|
};
|
|
};
|
|
|
|
security.polkit.extraConfig =
|
|
let
|
|
groupCheck =
|
|
(lib.concatStrings (map (g: "subject.isInGroup(\"${g}\") || ") cfg.IPCAllowedGroups)) + "false";
|
|
in
|
|
optionalString cfg.dbus.enable ''
|
|
polkit.addRule(function(action, subject) {
|
|
if ((action.id == "org.usbguard.Policy1.listRules" ||
|
|
action.id == "org.usbguard.Policy1.appendRule" ||
|
|
action.id == "org.usbguard.Policy1.removeRule" ||
|
|
action.id == "org.usbguard.Devices1.applyDevicePolicy" ||
|
|
action.id == "org.usbguard.Devices1.listDevices" ||
|
|
action.id == "org.usbguard1.getParameter" ||
|
|
action.id == "org.usbguard1.setParameter") &&
|
|
subject.active == true && subject.local == true &&
|
|
(${groupCheck})) {
|
|
return polkit.Result.YES;
|
|
}
|
|
});
|
|
'';
|
|
};
|
|
imports = [
|
|
(mkRemovedOptionModule [ "services" "usbguard" "IPCAccessControlFiles" ]
|
|
"The usbguard module now hardcodes IPCAccessControlFiles to /var/lib/usbguard/IPCAccessControl.d."
|
|
)
|
|
(mkRemovedOptionModule [
|
|
"services"
|
|
"usbguard"
|
|
"auditFilePath"
|
|
] "Removed usbguard module audit log files. Audit logs can be found in the systemd journal.")
|
|
(mkRenamedOptionModule
|
|
[ "services" "usbguard" "implictPolicyTarget" ]
|
|
[ "services" "usbguard" "implicitPolicyTarget" ]
|
|
)
|
|
];
|
|
}
|