From 945cce2f5e487492730e06e13e920e08c55d8084 Mon Sep 17 00:00:00 2001 From: Ophestra Umiker Date: Wed, 4 Sep 2024 17:03:21 +0900 Subject: [PATCH] nix: implement nixos module Signed-off-by: Ophestra Umiker --- flake.lock | 8 +- flake.nix | 59 ++++++-------- nixos.nix | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.nix | 25 ++++++ 4 files changed, 271 insertions(+), 41 deletions(-) create mode 100644 nixos.nix create mode 100644 package.nix diff --git a/flake.lock b/flake.lock index 98f71cb..06b3ed3 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1717179513, - "narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=", + "lastModified": 1725361206, + "narHash": "sha256-/HTUg+kMaqBPGrcQBYboAMsQHIWIkuKRDldss/035Hc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "63dacb46bf939521bdc93981b4cbb7ecb58427a0", + "rev": "2830c7c930311397d94c0b86a359c865c081c875", "type": "github" }, "original": { "owner": "NixOS", - "ref": "24.05", + "ref": "nixos-unstable-small", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 1e31f3b..aa10f3d 100644 --- a/flake.nix +++ b/flake.nix @@ -1,56 +1,41 @@ { - description = "fortify development environment"; + description = "fortify sandbox tool and nixos module"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/24.05"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable-small"; }; outputs = { self, nixpkgs }: let - supportedSystems = [ "x86_64-linux" ]; - forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + supportedSystems = [ + "aarch64-linux" + "i686-linux" + "x86_64-linux" + ]; + + forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); in { - devShells = forAllSystems ( + nixosModules.fortify = import ./nixos.nix; + + packages = forAllSystems ( system: let - pkgs = import nixpkgs { inherit system; }; + pkgs = nixpkgsFor.${system}; in { - default = - let - inherit (pkgs) - mkShell - buildGoModule - acl - xorg - ; - in - mkShell { - packages = [ - (buildGoModule rec { - pname = "fortify"; - version = "0.0.0-flake"; + default = self.packages.${system}.fortify; - src = ./.; - vendorHash = null; # we have no Go dependencies :3 - - ldflags = [ - "-s" - "-w" - "-X" - "main.Version=v${version}" - ]; - - buildInputs = [ - acl - xorg.libxcb - ]; - }) - ]; - }; + fortify = pkgs.callPackage ./package.nix { }; } ); + + devShells = forAllSystems (system: { + default = nixpkgsFor.${system}.mkShell { + buildInputs = with nixpkgsFor.${system}; [ self.packages.${system}.fortify ]; + }; + }); }; } diff --git a/nixos.nix b/nixos.nix new file mode 100644 index 0000000..0fe5b71 --- /dev/null +++ b/nixos.nix @@ -0,0 +1,220 @@ +{ + lib, + pkgs, + config, + ... +}: + +let + inherit (lib) + types + mkOption + mkEnableOption + mkIf + mapAttrs + mapAttrsToList + foldlAttrs + optional + ; + + cfg = config.environment.fortify; +in + +{ + options = { + environment.fortify = { + enable = mkEnableOption "fortify"; + + target = mkOption { + default = { }; + type = + let + inherit (types) + str + enum + bool + package + anything + submodule + listOf + attrsOf + nullOr + ; + in + attrsOf (submodule { + options = { + packages = mkOption { + type = listOf package; + default = [ ]; + description = '' + List of extra packages to install via home-manager. + ''; + }; + + launchers = mkOption { + type = attrsOf (submodule { + options = { + command = mkOption { + type = nullOr str; + default = null; + description = '' + Command to run as the target user. + Setting this to null will default command to wrapper name. + ''; + }; + + pulse = mkOption { + type = bool; + default = true; + description = '' + Whether to share the PulseAudio socket and cookie. + ''; + }; + + share = mkOption { + type = nullOr package; + default = null; + description = '' + Package containing share files. + Setting this to null will default package name to wrapper name. + ''; + }; + + method = mkOption { + type = enum [ + "fortify" + "fortify-sudo" + "sudo" + ]; + default = "fortify"; + description = '' + Launch method for the sandboxed program. + ''; + }; + }; + }); + default = { }; + }; + + persistence = mkOption { + type = submodule { + options = { + directories = mkOption { + type = listOf anything; + default = [ ]; + }; + + files = mkOption { + type = listOf anything; + default = [ ]; + }; + }; + }; + description = '' + Per-user state passed to github:nix-community/impermanence. + ''; + }; + + extraConfig = mkOption { + type = anything; + default = { }; + description = "Extra home-manager configuration."; + }; + }; + }); + }; + + package = mkOption { + type = types.package; + default = pkgs.callPackage ./package.nix { }; + description = "Package providing fortify."; + }; + + user = mkOption { + type = types.str; + description = "Privileged user account."; + }; + + shell = mkOption { + type = types.str; + description = '' + Shell set up to source home-manager for the privileged user. + Required for setting up the environment of sandboxed programs. + ''; + }; + + stateDir = mkOption { + type = types.str; + description = '' + The path to persistent storage where per-user state should be stored. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.persistence.${cfg.stateDir}.users = mapAttrs (_: target: target.persistence) cfg.target; + + home-manager.users = + mapAttrs (_: target: target.extraConfig // { home.packages = target.packages; }) cfg.target + // { + ${cfg.user}.home.packages = + let + wrap = + user: launchers: + mapAttrsToList ( + name: launcher: + let + command = if launcher.command == null then name else launcher.command; + in + pkgs.writeShellScriptBin name ( + if launcher.method == "sudo" then + '' + exec sudo -u ${user} -i ${command} $@ + '' + else + '' + exec fortify${if launcher.pulse then " -pulse" else ""} -u ${user}${ + if launcher.method == "fortify-sudo" then " -sudo" else "" + } ${cfg.shell} -c "exec ${command} $@" + '' + ) + ) launchers; + in + foldlAttrs ( + acc: user: target: + acc + ++ (foldlAttrs ( + shares: name: launcher: + let + pkg = if launcher.share != null then launcher.share else pkgs.${name}; + link = source: "[ -d '${source}' ] && ln -sv '${source}' $out/share || true"; + in + shares + ++ optional (launcher.method == "fortify") ( + pkgs.runCommand "${name}-share" { } '' + mkdir -p $out/share + ${link "${pkg}/share/applications"} + ${link "${pkg}/share/icons"} + ${link "${pkg}/share/man"} + '' + ) + ) (wrap user target.launchers) target.launchers) + ) [ cfg.package ] cfg.target; + }; + + security.polkit.extraConfig = + let + allowList = builtins.toJSON (mapAttrsToList (name: _: name) cfg.target); + in + '' + polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.machine1.host-shell" && + ${allowList}.indexOf(action.lookup("user")) > -1 && + subject.user == "${cfg.user}") { + return polkit.Result.YES; + } + }); + ''; + }; +} diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..4445d2c --- /dev/null +++ b/package.nix @@ -0,0 +1,25 @@ +{ + acl, + xorg, + buildGoModule, +}: + +buildGoModule rec { + pname = "fortify"; + version = "1.0.3"; + + src = ./.; + vendorHash = null; + + ldflags = [ + "-s" + "-w" + "-X" + "main.Version=v${version}" + ]; + + buildInputs = [ + acl + xorg.libxcb + ]; +}