at 24.11-pre 6.9 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.virtualisation.virtualbox.host; 7 8 virtualbox = cfg.package.override { 9 inherit (cfg) enableHardening headless enableWebService enableKvm; 10 extensionPack = if cfg.enableExtensionPack then pkgs.virtualboxExtpack else null; 11 }; 12 13 kernelModules = config.boot.kernelPackages.virtualbox.override { 14 inherit virtualbox; 15 }; 16 17in 18 19{ 20 options.virtualisation.virtualbox.host = { 21 enable = mkEnableOption "VirtualBox" // { 22 description = '' 23 Whether to enable VirtualBox. 24 25 ::: {.note} 26 In order to pass USB devices from the host to the guests, the user 27 needs to be in the `vboxusers` group. 28 ::: 29 ''; 30 }; 31 32 enableExtensionPack = mkEnableOption "VirtualBox extension pack" // { 33 description = '' 34 Whether to install the Oracle Extension Pack for VirtualBox. 35 36 ::: {.important} 37 You must set `nixpkgs.config.allowUnfree = true` in 38 order to use this. This requires you accept the VirtualBox PUEL. 39 ::: 40 ''; 41 }; 42 43 package = mkPackageOption pkgs "virtualbox" { }; 44 45 addNetworkInterface = mkOption { 46 type = types.bool; 47 default = true; 48 description = '' 49 Automatically set up a vboxnet0 host-only network interface. 50 ''; 51 }; 52 53 enableHardening = mkOption { 54 type = types.bool; 55 default = true; 56 description = '' 57 Enable hardened VirtualBox, which ensures that only the binaries in the 58 system path get access to the devices exposed by the kernel modules 59 instead of all users in the vboxusers group. 60 61 ::: {.important} 62 Disabling this can put your system's security at risk, as local users 63 in the vboxusers group can tamper with the VirtualBox device files. 64 ::: 65 ''; 66 }; 67 68 headless = mkOption { 69 type = types.bool; 70 default = false; 71 description = '' 72 Use VirtualBox installation without GUI and Qt dependency. Useful to enable on servers 73 and when virtual machines are controlled only via SSH. 74 ''; 75 }; 76 77 enableWebService = mkOption { 78 type = types.bool; 79 default = false; 80 description = '' 81 Build VirtualBox web service tool (vboxwebsrv) to allow managing VMs via other webpage frontend tools. Useful for headless servers. 82 ''; 83 }; 84 85 enableKvm = mkOption { 86 type = types.bool; 87 default = false; 88 description = '' 89 Enable KVM support for VirtualBox. This increases compatibility with Linux kernel versions, because the VirtualBox kernel modules 90 are not required. 91 92 This option is incompatible with `enableHardening` and `addNetworkInterface`. 93 94 Note: This is experimental. Please check https://github.com/cyberus-technology/virtualbox-kvm/issues. 95 ''; 96 }; 97 }; 98 99 config = mkIf cfg.enable (mkMerge [{ 100 warnings = mkIf (pkgs.config.virtualbox.enableExtensionPack or false) 101 ["'nixpkgs.virtualbox.enableExtensionPack' has no effect, please use 'virtualisation.virtualbox.host.enableExtensionPack'"]; 102 environment.systemPackages = [ virtualbox ]; 103 104 security.wrappers = let 105 mkSuid = program: { 106 source = "${virtualbox}/libexec/virtualbox/${program}"; 107 owner = "root"; 108 group = "vboxusers"; 109 setuid = true; 110 }; 111 executables = [ 112 "VBoxHeadless" 113 "VBoxNetAdpCtl" 114 "VBoxNetDHCP" 115 "VBoxNetNAT" 116 "VBoxVolInfo" 117 ] ++ (lib.optionals (!cfg.headless) [ 118 "VBoxSDL" 119 "VirtualBoxVM" 120 ]); 121 in mkIf cfg.enableHardening 122 (builtins.listToAttrs (map (x: { name = x; value = mkSuid x; }) executables)); 123 124 users.groups.vboxusers.gid = config.ids.gids.vboxusers; 125 126 services.udev.extraRules = 127 '' 128 SUBSYSTEM=="usb_device", ACTION=="add", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}" 129 SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh $major $minor $attr{bDeviceClass}" 130 SUBSYSTEM=="usb_device", ACTION=="remove", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor" 131 SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="${virtualbox}/libexec/virtualbox/VBoxCreateUSBNode.sh --remove $major $minor" 132 ''; 133 } (mkIf cfg.enableKvm { 134 assertions = [ 135 { 136 assertion = !cfg.addNetworkInterface; 137 message = "VirtualBox KVM only supports standard NAT networking for VMs. Please turn off virtualisation.virtualbox.host.addNetworkInferface."; 138 } 139 140 { 141 assertion = !cfg.enableHardening; 142 message = "VirtualBox KVM is not compatible with hardening: Please turn off virtualisation.virtualbox.host.enableHardening."; 143 } 144 ]; 145 146 warnings = [ 147 '' 148 KVM support in VirtualBox is experimental. Not all security features are available yet. 149 See: https://github.com/cyberus-technology/virtualbox-kvm/issues/12 150 '' 151 ]; 152 }) (mkIf (!cfg.enableKvm) { 153 boot.kernelModules = [ "vboxdrv" "vboxnetadp" "vboxnetflt" ]; 154 boot.extraModulePackages = [ kernelModules ]; 155 156 services.udev.extraRules = 157 '' 158 KERNEL=="vboxdrv", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" 159 KERNEL=="vboxdrvu", OWNER="root", GROUP="root", MODE="0666", TAG+="systemd" 160 KERNEL=="vboxnetctl", OWNER="root", GROUP="vboxusers", MODE="0660", TAG+="systemd" 161 ''; 162 163 # Since we lack the right setuid/setcap binaries, set up a host-only network by default. 164 }) (mkIf cfg.addNetworkInterface { 165 systemd.services.vboxnet0 = 166 { description = "VirtualBox vboxnet0 Interface"; 167 requires = [ "dev-vboxnetctl.device" ]; 168 after = [ "dev-vboxnetctl.device" ]; 169 wantedBy = [ "network.target" "sys-subsystem-net-devices-vboxnet0.device" ]; 170 path = [ virtualbox ]; 171 serviceConfig.RemainAfterExit = true; 172 serviceConfig.Type = "oneshot"; 173 serviceConfig.PrivateTmp = true; 174 environment.VBOX_USER_HOME = "/tmp"; 175 script = 176 '' 177 if ! [ -e /sys/class/net/vboxnet0 ]; then 178 VBoxManage hostonlyif create 179 cat /tmp/VBoxSVC.log >&2 180 fi 181 ''; 182 postStop = 183 '' 184 VBoxManage hostonlyif remove vboxnet0 185 ''; 186 }; 187 188 networking.interfaces.vboxnet0.ipv4.addresses = [{ address = "192.168.56.1"; prefixLength = 24; }]; 189 # Make sure NetworkManager won't assume this interface being up 190 # means we have internet access. 191 networking.networkmanager.unmanaged = ["vboxnet0"]; 192 }) (mkIf config.networking.useNetworkd { 193 systemd.network.networks."40-vboxnet0".extraConfig = '' 194 [Link] 195 RequiredForOnline=no 196 ''; 197 }) 198 199]); 200}