diff --git a/.gitignore b/.gitignore index e22dbe9..3a44330 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ # SPDX-License-Identifier: MIT /result* +/.zig-cache +/zig-out +/zig-pkg diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..1655421 --- /dev/null +++ b/build.zig @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: © 2023 Jeffrey C. Ollie +// SPDX-License-Identifier: MIT + +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const uid = b.option(u32, "uid", "uid to run as") orelse return error.MissingOption; + + const options = b.addOptions(); + options.addOption(u32, "uid", uid); + + const exe = b.addExecutable(.{ + .name = "execas", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + exe.root_module.addOptions("options", options); + + b.installArtifact(exe); + const run_step = b.step("run", "Run the app"); + + const run_cmd = b.addRunArtifact(exe); + run_step.dependOn(&run_cmd.step); + + run_cmd.step.dependOn(b.getInstallStep()); + + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const exe_tests = b.addTest(.{ + .root_module = exe.root_module, + }); + + const run_exe_tests = b.addRunArtifact(exe_tests); + + const test_step = b.step("test", "Run tests"); + test_step.dependOn(&run_exe_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..ea7f7c8 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: © 2023 Jeffrey C. Ollie +// SPDX-License-Identifier: MIT + +.{ + .name = .execas, + .version = "0.0.1", + .fingerprint = 0xd3da9c6c90740108, // Changing this has security and trust implications. + .minimum_zig_version = "0.16.0-dev.2596+469bf6af0", + .dependencies = .{}, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + "LICENSES", + }, +} diff --git a/flake.lock b/flake.lock index e0b1fad..83f2278 100644 --- a/flake.lock +++ b/flake.lock @@ -18,14 +18,16 @@ "nixpkgs": [ "nixpkgs" ], - "zig": "zig" + "zig": [ + "zig" + ] }, "locked": { - "lastModified": 1771090423, - "narHash": "sha256-LXgGCQwxq4FVFGz9y67u4mRJ2/IcR6DzGAeozibGzio=", + "lastModified": 1771097473, + "narHash": "sha256-rdwLgKpTuxXwoYZ+Bb4G3AGWllqsUa0hYebhy7rvRvU=", "ref": "refs/heads/main", - "rev": "16258dc104bead08b93ef424315f8404ba3cf34f", - "revCount": 17, + "rev": "66fddd025358abde9935f8d3398506bc854eeea1", + "revCount": 18, "type": "git", "url": "https://git.ocjtech.us/jeff/push-container.git" }, @@ -37,22 +39,22 @@ "root": { "inputs": { "nixpkgs": "nixpkgs", - "push-container": "push-container" + "push-container": "push-container", + "zig": "zig" } }, "zig": { "inputs": { "nixpkgs": [ - "push-container", "nixpkgs" ] }, "locked": { - "lastModified": 1771027958, - "narHash": "sha256-tpNoCFgtf+WNKUhiVuYF3ih7381ef/e8kRWrQmpLcSY=", + "lastModified": 1771099964, + "narHash": "sha256-xIg8XsZ5CqMrjmArTrcd1SEPm927gqqHAauLIs43bfk=", "ref": "refs/heads/main", - "rev": "844f43e0f29c47b2c6e690b4e6038774a032ed3e", - "revCount": 1619, + "rev": "4310fcb250a29bcb86ba95466754573ba033810e", + "revCount": 1620, "type": "git", "url": "https://git.ocjtech.us/jeff/zig-overlay.git" }, diff --git a/flake.nix b/flake.nix index c681bbf..2659911 100644 --- a/flake.nix +++ b/flake.nix @@ -10,6 +10,13 @@ }; push-container = { url = "git+https://git.ocjtech.us/jeff/push-container.git"; + inputs = { + nixpkgs.follows = "nixpkgs"; + zig.follows = "zig"; + }; + }; + zig = { + url = "git+https://git.ocjtech.us/jeff/zig-overlay.git"; inputs = { nixpkgs.follows = "nixpkgs"; }; @@ -21,12 +28,43 @@ self, nixpkgs, push-container, + zig, }: let makePackages = system: import nixpkgs { inherit system; + overlays = [ + # (final: prev: { + # util-linux = prev.util-linux.override { + # pamSupport = false; + # }; + # }) + # (final: prev: { + # nix = prev.nix.overrideAttrs (old: { + # postInstall = '' + # chmod u+s $out/bin/nix + # ''; + # }); + # }) + (final: prev: { + docker_29 = prev.docker_29.override { + clientOnly = true; + }; + }) + # (final: prev: { + # git = prev.git.override { + # perlSupport = false; + # pythonSupport = false; + # svnSupport = false; + # sendEmailSupport = false; + # withManual = false; + # withSsh = true; + # openssh = prev.openssh; + # }; + # }) + ]; }; forAllSystems = ( function: @@ -42,9 +80,9 @@ lib = pkgs.lib; in { - docker-client = pkgs.docker_28.override { - clientOnly = true; - }; + # docker-client = pkgs.docker_29.override { + # clientOnly = true; + # }; git = pkgs.git.override { perlSupport = false; pythonSupport = false; @@ -64,9 +102,11 @@ pkgs.bind.dnsutils pkgs.coreutils-full pkgs.curl + pkgs.docker_29 pkgs.forgejo-cli pkgs.gawk pkgs.gh + # pkgs.git pkgs.glibc pkgs.gnugrep pkgs.gnused @@ -82,6 +122,7 @@ pkgs.podman pkgs.reuse pkgs.regctl + pkgs.shadow.su pkgs.stdenv.cc.cc.lib pkgs.sudo pkgs.tailscale @@ -89,7 +130,8 @@ pkgs.xz pkgs.zstd - self.packages.${pkgs.stdenv.hostPlatform.system}.docker-client + # self.packages.${pkgs.stdenv.hostPlatform.system}.sudo + # self.packages.${pkgs.stdenv.hostPlatform.system}.docker-client self.packages.${pkgs.stdenv.hostPlatform.system}.git push-container.packages.${pkgs.stdenv.hostPlatform.system}.push-container ]; @@ -278,7 +320,7 @@ ''; sudoers = '' - root ALL=(ALL:ALL) SETENV:ALL + root ALL=(ALL:ALL) NOPASSWD:ALL SETENV:ALL %wheel ALL=(ALL:ALL) NOPASSWD:ALL SETENV:ALL ''; @@ -362,22 +404,35 @@ } '' mkdir -p $out/etc + mkdir -p $out/etc/ssl/certs ln -s /nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt $out/etc/ssl/certs + cat $passwdContentsPath > $out/etc/passwd echo "" >> $out/etc/passwd + cat $groupContentsPath > $out/etc/group echo "" >> $out/etc/group + cat $shadowContentsPath > $out/etc/shadow echo "" >> $out/etc/shadow + cat $sudoersPath > $out/etc/sudoers + echo "" >> $out/etc/sudoers + + mkdir -p $out/etc/pam.d + cat $pamSuPath > $out/etc/pam.d/su + echo "" >> $out/etc/pam.d/su + + mkdir -p $out/etc/nix + cat $nixConfContentsPath > $out/etc/nix/nix.conf + echo "" >> $out/etc/nix/nix.conf + mkdir -p $out/usr ln -s /nix/var/nix/profiles/share $out/usr/ mkdir -p $out/nix/var/nix/gcroots mkdir -p $out/tmp mkdir -p $out/var/tmp - mkdir -p $out/etc/nix - cat $nixConfContentsPath > $out/etc/nix/nix.conf mkdir -p $out/etc/containers mkdir -p $out/etc/containers/networks @@ -417,7 +472,6 @@ mkdir -p $out/bin $out/usr/bin ln -s ${pkgs.coreutils}/bin/env $out/usr/bin/env - # ln -s ${pkgs.bashInteractive}/bin/bash $out/bin/sh ''; in pkgs.dockerTools.buildLayeredImageWithNixDb { @@ -446,39 +500,52 @@ chmod u=rwxt,u=rwx,o=rwx tmp chmod u=rwxt,u=rwx,o=rwx var/tmp chown -R 1001:1001 github - chown -R 1001:1001 nix + # chown -R 1001:1001 nix ''; - config = { - Cmd = [ "${pkgs.bashInteractive}/bin/bash" ]; - User = "1001:1001"; - WorkingDir = "/github/home"; - Env = [ - "USER=github" - "PATH=${ - lib.concatStringsSep ":" [ - "/github/home/.nix-profile/bin" - "/nix/var/nix/profiles/default/bin" - "/nix/var/nix/profiles/default/sbin" - ] - }" - "MANPATH=${ - lib.concatStringsSep ":" [ - "/github/home/.nix-profile/share/man" - "/nix/var/nix/profiles/default/share/man" - ] - }" - "LD_LIBRARY_PATH=${ - pkgs.lib.makeLibraryPath [ - pkgs.glibc - pkgs.stdenv.cc.cc.lib - ] - }" - "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" - "GIT_SSL_CAINFO=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" - "NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" - "NIX_PATH=/nix/var/nix/profiles/per-user/github/channels:/github/home/.nix-defexpr/channels" - ]; - }; + config = + let + execas = pkgs.callPackage ./package.nix { + uid = 1001; + zig = zig.packages.${pkgs.stdenv.hostPlatform.system}.master; + }; + entrypoint = pkgs.writeShellScriptBin "setup" '' + ${lib.getExe pkgs.nix} daemon --trusted >/dev/null 2>&1 & + + exec ${lib.getExe execas} "$@" + ''; + in + { + Cmd = [ "${pkgs.bashInteractive}/bin/bash" ]; + User = "0:0"; + WorkingDir = "/github/home"; + Entrypoint = [ "${lib.getExe entrypoint}" ]; + Env = [ + "USER=github" + "PATH=${ + lib.concatStringsSep ":" [ + "/github/home/.nix-profile/bin" + "/nix/var/nix/profiles/default/bin" + "/nix/var/nix/profiles/default/sbin" + ] + }" + "MANPATH=${ + lib.concatStringsSep ":" [ + "/github/home/.nix-profile/share/man" + "/nix/var/nix/profiles/default/share/man" + ] + }" + "LD_LIBRARY_PATH=${ + pkgs.lib.makeLibraryPath [ + pkgs.glibc + pkgs.stdenv.cc.cc.lib + ] + }" + "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + "GIT_SSL_CAINFO=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + "NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" + "NIX_PATH=/nix/var/nix/profiles/per-user/github/channels:/github/home/.nix-defexpr/channels" + ]; + }; }; } ); @@ -490,6 +557,7 @@ pkgs.pinact pkgs.regctl pkgs.reuse + zig.packages.${pkgs.stdenv.hostPlatform.system}.master push-container.packages.${pkgs.stdenv.hostPlatform.system}.push-container ]; diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..5d108c8 --- /dev/null +++ b/package.nix @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie +# SPDX-License-Identifier: MIT + +{ + lib, + stdenv, + zig, + uid, + ... +}: +stdenv.mkDerivation (finalAttrs: { + name = "execas"; + src = lib.cleanSource ./.; + nativeBuildInputs = [ + zig + ]; + zigBuildFlags = [ + "-Duid=${toString uid}" + ]; + meta = { + mainProgram = "execas"; + license = lib.licenses.mit; + }; +}) diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..9e60c68 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: © 2023 Jeffrey C. Ollie +// SPDX-License-Identifier: MIT + +const std = @import("std"); +const options = @import("options"); + +pub fn main(init: std.process.Init) !void { + const arena: std.mem.Allocator = init.arena.allocator(); + const io = init.io; + + const rc = std.os.linux.setuid(options.uid); + switch (std.os.linux.errno(rc)) { + .SUCCESS => {}, + else => |err| return std.posix.unexpectedErrno(err), + } + + var argv: std.ArrayList([]const u8) = .empty; + + var it = try init.minimal.args.iterateAllocator(arena); + _ = it.next(); + + while (it.next()) |arg| { + try argv.append(arena, arg); + } + + const err = std.process.replace(io, .{ + .argv = argv.items, + }); + + std.debug.print("unable to execute: {t}\n", .{err}); +}