Newer
Older
# A NixOS module which enables remotely-triggered deployment updates.
{ config, lib, pkgs, ... }:
# A handy alias for our part of the configuration.
cfg = config.services.private-storage.deployment;
# Compute an authorized_keys line that allows the holder of a certain key to
# execute a certain command *only*.
restrictedKey =
{ authorizedKey, command, gridName }:
# `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=\"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 {
type = lib.types.str;
example = lib.literalExample ''
ssh-ed25519 AAAAC3N...
'';
description = ''
The SSH public key to authorize to trigger a deployment update.
'';
};
services.private-storage.deployment.gridName = lib.mkOption {
type = lib.types.str;
example = lib.literalExample "staging";
description = ''
The name of the grid configuration to use to update this deployment.
'';
};
};
config = {
# Configure the system to use our binary cache so that deployment updates
# only require downloading pre-built software, not building it ourselves.
nix = {
binaryCachePublicKeys = [
"saxtons.private.storage:MplOcEH8G/6mRlhlKkbA8GdeFR3dhCFsSszrspE/ZwY="
];
binaryCaches = [
"http://saxtons.private.storage"
];
};
# 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
# the temporary state morph creates to do the self-update.
services.logind.extraConfig = ''
RuntimeDirectorySize=50%
'';
# Configure the deployment user.
users.users.deployment = {
# A user must be either normal or system. A normal user uses the
# default shell, has a home directory created for it at the usual
# location, and is in the "users" group. That's pretty much what we
# want for the deployment user.
isNormalUser = true;
# Authorize the supplied key to run the deployment update command.
openssh.authorizedKeys.keys = [
(restrictedKey {
inherit (cfg) authorizedKey gridName;
];
};
};
}