diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 35f5e87d7e5..01bd2960517 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -694,6 +694,7 @@ ./services/x11/xserver.nix ./system/activation/activation-script.nix ./system/activation/top-level.nix + ./system/boot/binfmt.nix ./system/boot/coredump.nix ./system/boot/emergency-mode.nix ./system/boot/grow-partition.nix diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix new file mode 100644 index 00000000000..15e84dc021e --- /dev/null +++ b/nixos/modules/system/boot/binfmt.nix @@ -0,0 +1,139 @@ +{ config, lib, ... }: +let + inherit (lib) mkOption types optionalString; + + cfg = config.boot.binfmtMiscRegistrations; + + makeBinfmtLine = name: { recognitionType, offset, magicOrExtension + , mask, preserveArgvZero, openBinary + , matchCredentials, fixBinary, ... + }: let + type = if recognitionType == "magic" then "M" else "E"; + offset' = toString offset; + mask' = toString mask; + interpreter = "/run/binfmt/${name}"; + flags = if !(matchCredentials -> openBinary) + then throw "boot.binfmtMiscRegistrations.${name}: you can't specify openBinary = false when matchCredentials = true." + else optionalString preserveArgvZero "P" + + optionalString (openBinary && !matchCredentials) "O" + + optionalString matchCredentials "C" + + optionalString fixBinary "F"; + in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}"; + + binfmtFile = builtins.toFile "binfmt_nixos.conf" + (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine cfg)); + + activationSnippet = name: { interpreter, ... }: + "ln -sf ${interpreter} /run/binfmt/${name}"; + activationScript = '' + mkdir -p -m 0755 /run/binfmt + ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet cfg)} + ''; +in { + options = { + boot.binfmtMiscRegistrations = mkOption { + default = {}; + + description = '' + Extra binary formats to register with the kernel. + See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details. + ''; + + type = types.attrsOf (types.submodule ({ config, ... }: { + options = { + recognitionType = mkOption { + default = "magic"; + description = "Whether to recognize executables by magic number or extension."; + type = types.enum [ "magic" "extension" ]; + }; + + offset = mkOption { + default = null; + description = "The byte offset of the magic number used for recognition."; + type = types.nullOr types.int; + }; + + magicOrExtension = mkOption { + description = "The magic number or extension to match on."; + type = types.str; + }; + + mask = mkOption { + default = null; + description = + "A mask to be ANDed with the byte sequence of the file before matching"; + type = types.nullOr types.str; + }; + + interpreter = mkOption { + description = '' + The interpreter to invoke to run the program. + + Note that the actual registration will point to + /run/binfmt/''${name}, so the kernel interpreter length + limit doesn't apply. + ''; + type = types.path; + }; + + preserveArgvZero = mkOption { + default = false; + description = '' + Whether to pass the original argv[0] to the interpreter. + + See the description of the 'P' flag in the kernel docs + for more details; + ''; + type = types.bool; + }; + + openBinary = mkOption { + default = config.matchCredentials; + description = '' + Whether to pass the binary to the interpreter as an open + file descriptor, instead of a path. + ''; + type = types.bool; + }; + + matchCredentials = mkOption { + default = false; + description = '' + Whether to launch with the credentials and security + token of the binary, not the interpreter (e.g. setuid + bit). + + See the description of the 'C' flag in the kernel docs + for more details. + + Implies/requires openBinary = true. + ''; + type = types.bool; + }; + + fixBinary = mkOption { + default = false; + description = '' + Whether to open the interpreter file as soon as the + registration is loaded, rather than waiting for a + relevant file to be invoked. + + See the description of the 'F' flag in the kernel docs + for more details. + ''; + type = types.bool; + }; + }; + })); + }; + }; + + config = lib.mkIf (cfg != {}) { + environment.etc."binfmt.d/nixos.conf".source = binfmtFile; + system.activationScripts.binfmt = activationScript; + systemd.additionalUpstreamSystemUnits = + [ "proc-sys-fs-binfmt_misc.automount" + "proc-sys-fs-binfmt_misc.mount" + ]; + }; +}