X-Git-Url: http://git.scottworley.com/auto-upgrade-with-pinch/blobdiff_plain/f1a53b29b8269cb5dd28a3285bc95a7df37f9a16..13226f1c72df5771124e10f5de3648c3db0b587a:/modules/auto-upgrade.nix?ds=sidebyside diff --git a/modules/auto-upgrade.nix b/modules/auto-upgrade.nix index 7e48c40..0977176 100644 --- a/modules/auto-upgrade.nix +++ b/modules/auto-upgrade.nix @@ -1,65 +1,73 @@ +# auto-upgrade-with-pinch: Secure managed NixOS updates +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, version 3. + { config, lib, pkgs, ... }: with lib; let + local-pkgs = import ../. { inherit pkgs; }; cfg = config.system.autoUpgradeWithPinch; - pull-repo-script = - pkgs.writeShellScript "pull-repo" '' - set -eo pipefail - - path=$1 - config=$2 - - prop() { - ${pkgs.jq}/bin/jq -r ".$1" <<< "$config" - } - - echo Pulling in "$path" >&2 - - if [[ ! -e "$path" ]];then - d=$(mktemp -d) - ${pkgs.git}/bin/git init "$d" - ${pkgs.git}/bin/git -C "$d" checkout -b "$(prop localBranch)" - ${pkgs.git}/bin/git -C "$d" remote add "$(prop remoteName)" "$(prop url)" - ${pkgs.git}/bin/git -C "$d" branch -u "$(prop remoteBranch)" - mkdir -p "$(${pkgs.coreutils}/bin/dirname "$path")" - mv "$d" "$path" - fi - - cd "$path" - - if [[ "$(${pkgs.git}/bin/git remote get-url "$(prop remoteName)")" != "$(prop url)" ]]; then - echo Expected git remote "$(prop remoteName)" to point at "$(prop url)" \ - but it points at "$(${pkgs.git}/bin/git remote get-url "$(prop remoteName)")" >&2 - case "$(prop onRemoteURLMismatch)" in - abort) exit 1;; - update) echo Updating it >&2 - ${pkgs.git}/bin/git -C "$d" remote set-url "$(prop remoteName)" "$(prop url)";; - esac - fi - - ${pkgs.git}/bin/git fetch "$(prop remoteName)" "$(prop remoteBranch)" - - if [[ "$(${pkgs.git}/bin/git rev-parse --abbrev-ref HEAD)" != "$(prop localBranch)" ]];then - echo Could not merge because currently-checked-out \ - \""$(${pkgs.git}/bin/git rev-parse --abbrev-ref HEAD)"\" is not \ - \""$(prop localBranch)"\" - case "$(prop onBranchMismatch)" in - abort) exit 1;; - continue) exit 0;; - esac - fi - - if [[ "$(prop requireSignature)" == true ]]; then - ${pkgs.polite-merge}/bin/polite-merge \ - -c gpg.program='${pkgs.keyedgpg} '"$(prop 'signingKeys[]' | tr \\n ' ')"' --' \ - merge --ff-only --verify-signatures - else - ${pkgs.polite-merge}/bin/polite-merge merge --ff-only - fi - ''; + pull-repo-script = pkgs.writeShellScript "pull-repo" '' + set -eo pipefail + + path=$1 + config=$2 + + prop() { + ${pkgs.jq}/bin/jq -r ".$1" <<< "$config" + } + + echo Pulling in "$path" >&2 + + if [[ ! -e "$path" ]];then + d=$(mktemp -d) + ${pkgs.git}/bin/git init "$d" + ${pkgs.git}/bin/git -C "$d" checkout -b "$(prop localBranch)" + ${pkgs.git}/bin/git -C "$d" remote add "$(prop remoteName)" "$(prop url)" + ${pkgs.git}/bin/git -C "$d" branch -u "$(prop remoteBranch)" + mkdir -p "$(${pkgs.coreutils}/bin/dirname "$path")" + mv "$d" "$path" + fi + + cd "$path" + + if [[ "$(${pkgs.git}/bin/git remote get-url "$(prop remoteName)")" != "$(prop url)" ]]; then + echo Expected git remote "$(prop remoteName)" to point at "$(prop url)" \ + but it points at "$(${pkgs.git}/bin/git remote get-url "$(prop remoteName)")" >&2 + case "$(prop onRemoteURLMismatch)" in + abort) exit 1;; + update) echo Updating it >&2 + ${pkgs.git}/bin/git -C "$d" remote set-url "$(prop remoteName)" "$(prop url)";; + esac + fi + + ${pkgs.git}/bin/git fetch "$(prop remoteName)" "$(prop remoteBranch)" + + if [[ "$(${pkgs.git}/bin/git rev-parse --abbrev-ref HEAD)" != "$(prop localBranch)" ]];then + echo Could not merge because currently-checked-out \ + \""$(${pkgs.git}/bin/git rev-parse --abbrev-ref HEAD)"\" is not \ + \""$(prop localBranch)"\" + case "$(prop onBranchMismatch)" in + abort) exit 1;; + continue) exit 0;; + esac + fi + + if [[ "$(prop requireSignature)" == true ]]; then + ${pkgs.polite-merge}/bin/polite-merge \ + -c gpg.program=${escapeShellArg (local-pkgs.keyed-gpg cfg.signingKeys)} \ + merge --ff-only --verify-signatures + else + ${pkgs.polite-merge}/bin/polite-merge merge --ff-only + fi + ''; auto-upgrade-script = pkgs.writeShellScript "auto-upgrade" '' - ${pkgs.utillinux}/bin/flock /run/auto-upgrade-with-pinch ${ + ${pkgs.coreutils}/bin/nice -n 17 \ + ${pkgs.util-linux}/bin/ionice -c 3 \ + ${pkgs.util-linux}/bin/flock /run/auto-upgrade-with-pinch ${ pkgs.writeShellScript "auto-upgrade-with-lock-held" '' set -eo pipefail @@ -144,12 +152,12 @@ let + concatMapStringsSep "\n" (f: "verify_ownership ${escapeShellArg f}") cfg.upgradeConfig)} - config=$(${pkgs.nix}/bin/nix eval --json -f ${../upgrade-config.nix} \ + config=$(${pkgs.nix}/bin/nix-instantiate --eval --strict --json -A config \ --arg upgradeConfig ${ escapeShellArg ("[" + lib.concatMapStringsSep " " lib.strings.escapeNixString cfg.upgradeConfig + "]") - } config) + } ${../upgrade-config.nix}) config_query() { ${pkgs.jq}/bin/jq -r "$@" <<< "$config" @@ -175,8 +183,10 @@ let # Build in_tmpdir hydrate ${config.system.build.nixos-rebuild}/bin/nixos-rebuild build while read user;do + pushd / hydrate /run/wrappers/bin/sudo -u "$user" \ ${pkgs.nix}/bin/nix-build --no-out-link '' -A "$(userenv_query "$user" .package)" + popd done < <( config_query '.userEnvironments | keys []' ) # Install @@ -218,6 +228,15 @@ in { ''; }; + signingKeys = mkOption { + type = types.listOf types.path; + description = '' + Files containing GPG keys that are authorized to sign updates. + Updates are only merged if the commit at the tip of the remote + ref is signed with one of these keys. + ''; + }; + upgradeConfig = mkOption { type = types.listOf types.path; description = '' @@ -261,7 +280,6 @@ in { ''; nixpkgs.overlays = [ - (import ../overlays/keyedgpg.nix) (import ../overlays/pinch.nix) (import ../overlays/polite-merge.nix) (self: super: {