# A NixOS module which enables remotely-triggered deployment updates. { config, lib, ... }: let # 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=\"${command} ${gridName}\" ${authorizedKey}"; 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" ]; }; # 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 = { # Without some shell no login is possible at all, even to execute our # single non-restricted command. useDefaultShell = true; # Without a home directory, lots of tools break. createHome = true; home = "/home/deployment"; # Authorize the supplied key to run the deployment update command. openssh.authorizedKeys.keys = [ (restrictedKey { inherit (cfg) authorizedKey gridName; command = ./update-deployment; }) ]; }; }; }