diff --git a/flake.lock b/flake.lock index 255acbb..9cc0d4f 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1763938834, - "narHash": "sha256-j8iB0Yr4zAvQLueCZ5abxfk6fnG/SJ5JnGUziETjwfg=", + "lastModified": 1764782380, + "narHash": "sha256-9tpiB/ta9m4fNXAjmgrLOCOHXsqUYq4mTPcBYSUDvqk=", "owner": "ipetkov", "repo": "crane", - "rev": "d9e753122e51cee64eb8d2dddfe11148f339f5a2", + "rev": "e09877b775852470808fda69120691647960bcf2", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1762363567, - "narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=", + "lastModified": 1764667669, + "narHash": "sha256-7WUCZfmqLAssbDqwg9cUDAXrSoXN79eEEq17qhTNM/Y=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4", + "rev": "418468ac9527e799809c900eda37cbff999199b6", "type": "github" }, "original": { @@ -45,11 +45,11 @@ ] }, "locked": { - "lastModified": 1762655942, - "narHash": "sha256-hOM12KcQNQALrhB9w6KJmV5hPpm3GA763HRe9o7JUiI=", + "lastModified": 1764729618, + "narHash": "sha256-z4RA80HCWv2los1KD346c+PwNPzMl79qgl7bCVgz8X0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "6ac961b02d4235572692241e333d0470637f5492", + "rev": "52764074a85145d5001bf0aa30cb71936e9ad5b8", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 43157a5..5d04334 100644 --- a/flake.nix +++ b/flake.nix @@ -35,15 +35,29 @@ in f system pkgs toolchain); in { apps = let - mkApp = program: { type = "app"; program = toString program; }; + mkApp = program: description: { + type = "app"; + program = toString program; + meta = { + inherit description; + }; + }; + mkVm = name: mkApp "${self.nixosConfigurations.${name}.config.system.build.vm}/bin/run-nixos-vm"; in forAllSystems (system: pkgs: _: { - muscl = mkApp (lib.getExe self.packages.${system}.muscl); + muscl = mkApp (lib.getExe self.packages.${system}.muscl) "Run muscl without any setup"; coverage = mkApp (pkgs.writeShellScript "muscl-coverage" '' ${lib.getExe pkgs.python3} -m http.server -d "${self.packages.${system}.coverage}/html" - ''); - vm = mkApp "${self.nixosConfigurations.vm.config.system.build.vm}/bin/run-nixos-vm"; + '') "Serve code coverage report at http://localhost:8000"; + + vm = mkVm "vm" "Start a NixOS VM with muscl installed"; + vm-suid = mkVm "vm-suid" "Start a NixOS VM with muscl as SUID/SGID installed"; }); + nixosConfigurations = { + vm = import ./nix/nixos-configurations/vm.nix { inherit self nixpkgs; }; + vm-suid = import ./nix/nixos-configurations/vm-suid.nix { inherit self nixpkgs; }; + }; + devShell = forAllSystems (system: pkgs: toolchain: pkgs.mkShell { nativeBuildInputs = with pkgs; [ toolchain @@ -66,6 +80,12 @@ muscl-crane = final: prev: { muscl = self.packages.${prev.stdenv.hostPlatform.system}.muscl-crane; }; + muscl-suid = final: prev: { + muscl = self.packages.${prev.stdenv.hostPlatform.system}.muscl-suid; + }; + muscl-suid-crane = final: prev: { + muscl = self.packages.${prev.stdenv.hostPlatform.system}.muscl-suid-crane; + }; }; nixosModules = { @@ -87,83 +107,32 @@ }; in { default = self.packages.${system}.muscl-crane; + muscl = pkgs.callPackage ./nix/default.nix { inherit cargoToml cargoLock src; }; muscl-crane = pkgs.callPackage ./nix/default.nix { useCrane = true; inherit cargoToml cargoLock src craneLib; }; + + muscl-suid = pkgs.callPackage ./nix/default.nix { + suidSgidSupport = true; + inherit cargoToml cargoLock src; + }; + muscl-suid-crane = pkgs.callPackage ./nix/default.nix { + useCrane = true; + suidSgidSupport = true; + inherit cargoToml cargoLock src craneLib; + }; + coverage = pkgs.callPackage ./nix/coverage.nix { inherit cargoToml cargoLock src; }; filteredSource = pkgs.runCommandLocal "filtered-source" { } '' ln -s ${src} $out ''; }); - nixosConfigurations.vm = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - pkgs = import nixpkgs { - system = "x86_64-linux"; - overlays = [ - self.overlays.muscl-crane - ]; - }; - modules = [ - "${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" - "${nixpkgs}/nixos/tests/common/user-account.nix" - - self.nixosModules.default - - ({ config, pkgs, ... }: { - system.stateVersion = config.system.nixos.release; - virtualisation.graphics = false; - - users = { - groups = { - a = { }; - b = { }; - }; - users.alice.extraGroups = [ - "a" - "b" - "wheel" - "systemd-journal" - ]; - extraUsers.root.password = "root"; - }; - - services.getty.autologinUser = "alice"; - - users.motd = '' - ================================= - Welcome to the muscl vm! - - Try running: - ${config.services.muscl.package.meta.mainProgram} - - Password for alice is 'foobar' - Password for root is 'root' - - To exit, press Ctrl+A, then X - ================================= - ''; - - services.mysql = { - enable = true; - package = pkgs.mariadb; - }; - services.muscl = { - enable = true; - logLevel = "trace"; - createLocalDatabaseUser = true; - }; - - programs.vim = { - enable = true; - defaultEditor = true; - }; - - environment.systemPackages = with pkgs; [ jq ]; - }) - ]; - }; + checks = forAllSystems (system: pkgs: _: { + # NOTE: the non-crane build runs tests during checkPhase + inherit (self.packages.${system}) muscl muscl-suid; + }); }; } diff --git a/nix/default.nix b/nix/default.nix index 3a5e559..36f04c0 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -1,26 +1,63 @@ { lib , rustPlatform +, stdenv +, installShellFiles +, versionCheckHook + , cargoToml , cargoLock , src -, installShellFiles , useCrane ? false , craneLib ? null +, suidSgidSupport ? false }: let mainProgram = (lib.head cargoToml.bin).name; buildFunction = if useCrane then craneLib.buildPackage else rustPlatform.buildRustPackage; - cargoLock' = if useCrane then cargoLock else { lockFile = cargoLock; }; - pname = if useCrane then "${cargoToml.package.name}-crane" else cargoToml.package.name; -in -buildFunction { - pname = pname; - version = cargoToml.package.version; - inherit src; - cargoLock = cargoLock'; + pnameCraneSuffix = lib.optionalString useCrane "-crane"; + pnameSuidSuffix = lib.optionalString suidSgidSupport "-suid"; + pname = "${cargoToml.package.name}${pnameSuidSuffix}${pnameCraneSuffix}"; + + rustPlatformArgs = { + buildFeatures = lib.optional suidSgidSupport "suid-sgid-mode"; + cargoLock.lockFile = cargoLock; + + doCheck = true; + useNextest = true; + nativeCheckInputs = [ + versionCheckHook + ]; + cargoCheckFeatures = lib.optional suidSgidSupport "suid-sgid-mode"; + + postCheck = lib.optionalString (stdenv.buildPlatform.system == stdenv.hostPlatform.system && suidSgidSupport) '' + ./target/${stdenv.hostPlatform.rust.rustcTarget}/release/muscl --version | grep "SUID/SGID mode: enabled" + ''; + }; + + craneArgs = { + cargoLock = cargoLock; + cargoExtraArgs = lib.escapeShellArgs [ "--features" (lib.concatStringsSep "," (lib.optional suidSgidSupport "suid-sgid-mode")) ]; + cargoArtifacts = craneLib.buildDepsOnly { + inherit pname; + inherit (cargoToml.package) version; + src = lib.fileset.toSource { + root = ../.; + fileset = lib.fileset.unions [ + (craneLib.fileset.cargoTomlAndLock ../.) + ]; + }; + + cargoLock = cargoLock; + }; + }; +in +buildFunction ({ + inherit pname; + inherit (cargoToml.package) version; + inherit src; nativeBuildInputs = [ installShellFiles ]; postInstall = let @@ -29,10 +66,9 @@ buildFunction { export PATH="$out/bin:$PATH" export COMPLETE="${shell}" "${command}" > "$TMP/${command}.${shell}" - - # See https://github.com/clap-rs/clap/issues/1764 - sed -i 's/muscl/${command}/g' "$TMP/${command}.${shell}" ) + # See https://github.com/clap-rs/clap/issues/1764 + sed -i 's/muscl/${command}/g' "$TMP/${command}.${shell}" installShellCompletion "--${shell}" --cmd "${command}" "$TMP/${command}.${shell}" '') { shell = [ "bash" "zsh" "fish" ]; @@ -56,3 +92,6 @@ buildFunction { inherit mainProgram; }; } +// +(if useCrane then craneArgs else rustPlatformArgs) +) diff --git a/nix/nixos-configurations/vm-suid.nix b/nix/nixos-configurations/vm-suid.nix new file mode 100644 index 0000000..86e7fbf --- /dev/null +++ b/nix/nixos-configurations/vm-suid.nix @@ -0,0 +1,96 @@ +{ self, nixpkgs, ... }: +let + inherit (nixpkgs) lib; +in +nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + self.overlays.muscl-suid-crane + ]; + }; + modules = [ + "${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" + "${nixpkgs}/nixos/tests/common/user-account.nix" + + ({ config, pkgs, ... }: { + system.stateVersion = config.system.nixos.release; + virtualisation.graphics = false; + + users = { + groups = { + a = { }; + b = { }; + muscl = { }; + }; + users.muscl = { + isSystemUser = true; + group = "muscl"; + }; + users.alice.extraGroups = [ + "a" + "b" + "wheel" + "systemd-journal" + ]; + extraUsers.root.password = "root"; + }; + + services.getty.autologinUser = "alice"; + + users.motd = '' + ================================= + Welcome to the muscl SUID/SGID vm! + + Try running: + ${pkgs.muscl.meta.mainProgram} + + Password for alice is 'foobar' + Password for root is 'root' + + To exit, press Ctrl+A, then X + ================================= + ''; + + services.mysql = { + enable = true; + package = pkgs.mariadb; + ensureUsers = [ + { + name = "muscl"; + ensurePermissions = { + "mysql.*" = "SELECT, INSERT, UPDATE, DELETE"; + "*.*" = "GRANT OPTION, CREATE, DROP"; + }; + } + ]; + }; + + security.wrappers.muscl = { + owner = "muscl"; + group = "muscl"; + setuid = true; + source = lib.getExe pkgs.muscl; + }; + + environment.etc."muscl/config.toml".source = (pkgs.formats.toml { }).generate "muscl-config.toml" { + mysql = { + username = "muscl"; + password = "snakeoil"; + socket_path = "/run/mysqld/mysqld.sock"; + }; + }; + + # TODO: extra setup commands: + # set password for mysql user + + programs.vim = { + enable = true; + defaultEditor = true; + }; + + environment.systemPackages = with pkgs; [ jq ]; + }) + ]; +} diff --git a/nix/nixos-configurations/vm.nix b/nix/nixos-configurations/vm.nix new file mode 100644 index 0000000..11c8b2a --- /dev/null +++ b/nix/nixos-configurations/vm.nix @@ -0,0 +1,68 @@ +{ self, nixpkgs, ... }: +nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + self.overlays.muscl-crane + ]; + }; + modules = [ + "${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix" + "${nixpkgs}/nixos/tests/common/user-account.nix" + + self.nixosModules.default + + ({ config, pkgs, ... }: { + system.stateVersion = config.system.nixos.release; + virtualisation.graphics = false; + + users = { + groups = { + a = { }; + b = { }; + }; + users.alice.extraGroups = [ + "a" + "b" + "wheel" + "systemd-journal" + ]; + extraUsers.root.password = "root"; + }; + + services.getty.autologinUser = "alice"; + + users.motd = '' + ================================= + Welcome to the muscl vm! + + Try running: + ${config.services.muscl.package.meta.mainProgram} + + Password for alice is 'foobar' + Password for root is 'root' + + To exit, press Ctrl+A, then X + ================================= + ''; + + services.mysql = { + enable = true; + package = pkgs.mariadb; + }; + services.muscl = { + enable = true; + logLevel = "trace"; + createLocalDatabaseUser = true; + }; + + programs.vim = { + enable = true; + defaultEditor = true; + }; + + environment.systemPackages = with pkgs; [ jq ]; + }) + ]; +}