Skip to content
Snippets Groups Projects
Commit a3381024 authored by Tom Prince's avatar Tom Prince
Browse files

Don't use morph on nodes.

parent e6ee6b33
No related branches found
No related tags found
No related merge requests found
Pipeline #992 passed
# 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; }
......@@ -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;
})
];
};
......
#!/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
# 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment