From a33810244577d5f59c7c05d73f52bc8dc55998d8 Mon Sep 17 00:00:00 2001 From: Tom Prince <tom.prince@private.storage> Date: Thu, 2 Sep 2021 16:11:04 -0600 Subject: [PATCH] Don't use morph on nodes. --- morph/grid/local/public-keys/users.nix | 4 +- nixos/modules/deployment.nix | 69 ++++++----------- nixos/modules/update-deployment | 100 ------------------------- nixos/top-level.nix | 25 +++++++ 4 files changed, 48 insertions(+), 150 deletions(-) delete mode 100755 nixos/modules/update-deployment create mode 100644 nixos/top-level.nix diff --git a/morph/grid/local/public-keys/users.nix b/morph/grid/local/public-keys/users.nix index 412077c0..2a71d33c 100644 --- a/morph/grid/local/public-keys/users.nix +++ b/morph/grid/local/public-keys/users.nix @@ -1,4 +1,4 @@ # Add your public key. Example: -# let key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHx7wJQNqKn8jOC4AxySRL2UxidNp7uIK9ad3pMb1ifF flo@fs-la"; -let key = undefined; +let key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHx7wJQNqKn8jOC4AxySRL2UxidNp7uIK9ad3pMb1ifF flo@fs-la"; +#let key = undefined; in { "root" = key; "vagrant" = key; } diff --git a/nixos/modules/deployment.nix b/nixos/modules/deployment.nix index b0a5e3c4..c8599f5e 100755 --- a/nixos/modules/deployment.nix +++ b/nixos/modules/deployment.nix @@ -11,7 +11,16 @@ let # `restrict` means "disable all the things" then `command` means "but # enable running this one command" (the client does not have to supply the # command; if they authenticate, this is the command that will run). - "restrict,command=\"${command} ${gridName}\" ${authorizedKey}"; + "restrict,command=\"sudo ${command}\" ${authorizedKey}"; + + update-script = pkgs.writeShellScript "update-deployment" + '' + set -euxo pipefail + + nix-env --profile /nix/var/nix/profiles/system --set -f ${lib.escapeShellArg ../top-level.nix} --argstr gridname ${lib.escapeShellArg cfg.gridName} --argstr hostname ${lib.escapeShellArg config.networking.hostName} + + /nix/var/nix/profiles/system/bin/switch-to-configuration switch + ''; in { options = { services.private-storage.deployment.authorizedKey = lib.mkOption { @@ -44,46 +53,16 @@ in { ]; }; - # Create a one-time service that will set up an ssh key that allows the - # deployment user to authorize as root to perform the system update with - # `morph deploy`. - systemd.services.authorize-morph-as-root = { - enable = true; - serviceConfig = { - # Tell systemd that the service is a process that runs and then exits. - # By being "oneshot" instead of "simple" any dependencies are not - # started until after the process exits. We have no dependencies yet - # but if we did it would be more correct for them to wait until we are - # done. - # - # It is not clear that "oneshot" means "run once" though (maybe it - # does, I can't tell) so the script is robust in the face of repeated - # runs even though it should only ever need to be run once. - Type = "oneshot"; - }; - wantedBy = [ - # Run this to reach the multi-user target, a good target that is - # reached in the typical course of system startup. - "multi-user.target" - ]; - # Here's the program to run for this unit. It's a shell script that - # creates an ssh key that authorized root access via ssh and give it to - # the deployment user. If such a key appears to exist already, do - # nothing. - script = '' - KEY=~deployment/.ssh/morph_key - TMP="$KEY"_tmp - if [ ! -e "$KEY" ]; then - mkdir -p ~deployment/.ssh ~root/.ssh - chown deployment ~deployment/.ssh - ${pkgs.openssh}/bin/ssh-keygen -f "$TMP" - cat "$TMP".pub >> ~root/.ssh/authorized_keys - mv "$TMP".pub "$KEY".pub - mv "$TMP" "$KEY" - chown deployment "$KEY" - fi - ''; - }; + # This package is used by top-level.nix above. + system.extraDependencies = [ pkgs.morph.lib ]; + + security.sudo.extraRules = [ + { + users = ["deployment"]; + commands = [ "${update-script}" ]; + runAs = "root"; + } + ]; # Raise the hard-limit on the size of $XDG_RUNTIME_DIR (ie # /run/user/<uid>). The default of 10% is too small on some systems for @@ -100,17 +79,11 @@ in { # want for the deployment user. isNormalUser = true; - packages = [ - # update-deployment dependencies - pkgs.morph - pkgs.git - ]; - # Authorize the supplied key to run the deployment update command. openssh.authorizedKeys.keys = [ (restrictedKey { inherit (cfg) authorizedKey gridName; - command = ./update-deployment; + command = update-script; }) ]; }; diff --git a/nixos/modules/update-deployment b/nixos/modules/update-deployment deleted file mode 100755 index d8d32ff6..00000000 --- a/nixos/modules/update-deployment +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash - -set -euxo pipefail - -# Accept the name of the grid this system is part of as a parameter. This -# lets us pick the correct morph grid source file later on. -GRIDNAME=$1 -shift - -# Determine the right branch name to use for the particular grid we've been -# told we belong to. The grid name is a parameter to this script we can -# re-use it across all of our grids. See deployment.nix for the ssh -# configuration that controls what value is actually passed when an update is -# triggered. -case "${GRIDNAME}" in - "local") - BRANCH="develop" - ;; - - "testing") - BRANCH="staging" - ;; - - "production") - BRANCH="production" - ;; - - *) - echo "Unknown grid: ${GRIDNAME}" - exit 1 -esac - -# This is where we will maintain a checkout of PrivateStorageio for morph to -# use to compute the desired state. -CHECKOUT="${HOME}/PrivateStorageio" - -# This is the address of the git remote where we can get the latest -# PrivateStorageio. -REPO="https://whetstone.privatestorage.io/privatestorage/PrivateStorageio.git" - -if [ -e "${CHECKOUT}" ]; then - # It exists already so just make sure it contains the latest changes from - # the canonical repository. - git -C "${CHECKOUT}" fetch -else - # It doesn't exist so clone it. - git clone "${REPO}" "${CHECKOUT}" -fi - -# Get us to a pristine checkout of the right branch. -git -C "${CHECKOUT}" reset --hard "origin/${BRANCH}" - -# If we happen to be on the local grid then fix the undefined key. -if [ "${GRIDNAME}" = "local" ]; then - KEY="$(cat /etc/ssh/authorized_keys.d/vagrant)" - sed -i "s_undefined_\"${KEY}\"_" "${CHECKOUT}"/morph/grid/${GRIDNAME}/public-keys/users.nix -fi - -# Compute a log message explaining what we're doing. -LOG_MESSAGE="$(date --iso-8601=seconds) $(git -C "${CHECKOUT}" rev-parse HEAD)" - -# Make sure we use the right credentials and ask for the right account when -# morph makes the connection. morph's deployment target for each host is the -# full domain name (even though the host is only named with the unqualified -# hostname in the morph grid definition) so compute an ssh config section that -# matches that. Regardless, point this effort at localhost because we *know* -# it's just us we want to update. -cat > ~/.ssh/config <<EOF -Host $(hostname).$(domainname) - HostName 127.0.0.1 - IdentityFile ~/.ssh/morph_key - User root -EOF - -# Make sure known_hosts has the host key in it. -ssh -o StrictHostKeyChecking=no "$(hostname).$(domainname)" ":" - -# Set nixpkgs to our preferred version for the morph build. Annoyingly, we -# can't just use nixpkgs-2105.nix as our nixpkgs because some code (in morph, -# at least) wants <nixpkgs> to be a fully-resolved path to a nixpkgs tree. -# For example, morph evaluated `import <nixpkgs/lib>` which would turn into -# something like `import nixpkgs-2105.nix/lib` which is nonsense. -# -# So instead, import our nixpkgs which forces it to be instantiated in the -# store, then ask for its path, then set NIX_PATH to that. -export NIX_PATH="nixpkgs=$(nix eval "(import ${CHECKOUT}/nixpkgs-2105.nix { }).path")" - -# Attempt to update just this host. Choose the morph grid definition matching -# the grid we belong to and limit the morph deployment update to the host -# matching our name. morph uses just the bare hostname without the domain -# part. -if morph deploy "${CHECKOUT}"/morph/grid/"${GRIDNAME}"/grid.nix switch --on "$(hostname)"; then - # The deployment succeeded. Record success along with context we pre-computed. - echo "${LOG_MESSAGE} OK" >> ${HOME}/updates.txt - exit 0 -else - # Oops. Not so fortunate. Record failure. - echo "${LOG_MESSAGE} FAIL" >> ${HOME}/updates.txt - exit 1 -fi diff --git a/nixos/top-level.nix b/nixos/top-level.nix new file mode 100644 index 00000000..1e067bc3 --- /dev/null +++ b/nixos/top-level.nix @@ -0,0 +1,25 @@ +# A NixOS expression that evaluates to top-level system derivation for a node. +{ + # The gridname this node belongs to. This should correspond to the + # subdirectory of `morph/grid/` this corresponds to. + gridname, + # The hostname of this node. + hostname, +}: +let + branch = { + local = "develop"; + testing = "staging"; + production = "production"; + }.${gridname}; + checkout = builtins.fetchTarball { + name = "private.storage"; + url = "https://whetstone.privatestorage.io/privatestorage/PrivateStorageio/-/archive/${branch}/privatestorage.tar.bz2"; + }; + pkgs = import "${checkout}/nixpkgs-2105.nix" {}; + + morph = (import "${pkgs.morph.lib}/eval-machines.nix") { + networkExpr = "${checkout}/morph/grid/${gridname}/grid.nix"; + }; +in + morph.nodes.${hostname}.config.system.build.toplevel -- GitLab