fix(deploy): set up 'colmena' wrapper for hardware-backed SSH keys
This commit is contained in:
@@ -45,6 +45,7 @@
|
|||||||
taskwarrior.enable = true;
|
taskwarrior.enable = true;
|
||||||
audio.enable = true;
|
audio.enable = true;
|
||||||
ssh.enable = true;
|
ssh.enable = true;
|
||||||
|
deploy.enable = true;
|
||||||
music.enable = true;
|
music.enable = true;
|
||||||
terminal.enable = true;
|
terminal.enable = true;
|
||||||
devenv.enable = true;
|
devenv.enable = true;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
bash.addBinToPath = true;
|
bash.addBinToPath = true;
|
||||||
};
|
};
|
||||||
ssh.enable = true;
|
ssh.enable = true;
|
||||||
|
deploy.enable = true;
|
||||||
taskwarrior.enable = true;
|
taskwarrior.enable = true;
|
||||||
terminal.enable = true;
|
terminal.enable = true;
|
||||||
reference-manager.enable = true;
|
reference-manager.enable = true;
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# `colmena` wrapper script that opens a short-lived SSH master process for
|
||||||
|
# `colmena` to use so it works with hardware-backed key touch+PIN. assumes
|
||||||
|
# ControlPath ~/.ssh/socket-%r@%h:%p (see ./default.nix)
|
||||||
|
|
||||||
|
selector=""
|
||||||
|
want_on=0
|
||||||
|
on_given=0
|
||||||
|
deploy_cmd=0
|
||||||
|
nodes=()
|
||||||
|
on_nodes=()
|
||||||
|
master_processes=()
|
||||||
|
|
||||||
|
# build node list from `--on` value, a comma-separated string (e.g. "host-01,host-02")
|
||||||
|
for arg in "$@"; do
|
||||||
|
if ((want_on)); then
|
||||||
|
selector="$arg"
|
||||||
|
want_on=0
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
case "$arg" in
|
||||||
|
--on=*)
|
||||||
|
selector="${arg#--on=}"
|
||||||
|
on_given=1
|
||||||
|
;;
|
||||||
|
--on) want_on=1 on_given=1 ;;
|
||||||
|
apply | exec | upload-keys) deploy_cmd=1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
|
if ((deploy_cmd)) && ! ((on_given)); then
|
||||||
|
on_nodes=("${!_colmena_node_tags[@]}")
|
||||||
|
else
|
||||||
|
IFS=',' read -ra nodes <<< "$selector"
|
||||||
|
for node in "${nodes[@]}"; do
|
||||||
|
for node_tag in "${!_colmena_node_tags[@]}"; do
|
||||||
|
if [[ "$node" == @* ]]; then
|
||||||
|
for tag in ${_colmena_node_tags[$node_tag]}; do
|
||||||
|
# shellcheck disable=SC2053
|
||||||
|
[[ "$tag" == ${node#@} ]] && on_nodes+=("$node_tag")
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2053
|
||||||
|
[[ "$node_tag" == $node ]] && on_nodes+=("$node_tag")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
for node in "${on_nodes[@]}"; do
|
||||||
|
# check if master process is running (see `man ssh`)
|
||||||
|
ssh -O check "$node" 2>/dev/null && continue
|
||||||
|
|
||||||
|
# place ssh client into "master" mode for connection sharing (see `man ssh`)
|
||||||
|
if ssh -fNM "$node"; then
|
||||||
|
master_processes+=("$node")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
@colmena@ "$@" || rc=$?
|
||||||
|
|
||||||
|
# request master processes to exit (see `man ssh`)
|
||||||
|
for process in "${master_processes[@]}"; do
|
||||||
|
ssh -O exit "$process" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
exit "$rc"
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
myUtils,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.deploy;
|
||||||
|
|
||||||
|
hostDir = ../../../hosts;
|
||||||
|
hostNames = myUtils.dirNames hostDir;
|
||||||
|
hostsWithKeys = lib.filter (
|
||||||
|
hostname: builtins.pathExists (hostDir + "/${hostname}/ssh_host.pub")
|
||||||
|
) hostNames;
|
||||||
|
|
||||||
|
colmena = inputs.colmena.packages.${pkgs.stdenv.hostPlatform.system}.colmena;
|
||||||
|
|
||||||
|
hostTags = hostname: (myUtils.hostMeta (hostDir + "/${hostname}")).tags;
|
||||||
|
remoteHostsWithKeys = lib.filter (
|
||||||
|
hostname: !(builtins.elem "local" (hostTags hostname))
|
||||||
|
) hostsWithKeys;
|
||||||
|
nodeTagsDecl = ''
|
||||||
|
declare -A _colmena_node_tags=(
|
||||||
|
${lib.concatMapStringsSep "\n" (
|
||||||
|
hostname: " [${hostname}]=${lib.escapeShellArg (lib.concatStringsSep " " (hostTags hostname))}"
|
||||||
|
) remoteHostsWithKeys}
|
||||||
|
)
|
||||||
|
'';
|
||||||
|
|
||||||
|
colmenaWrapped = pkgs.writeShellApplication {
|
||||||
|
name = "colmena";
|
||||||
|
runtimeInputs = [ pkgs.openssh ];
|
||||||
|
text =
|
||||||
|
nodeTagsDecl
|
||||||
|
+ lib.replaceStrings [ "@colmena@" ] [ "${colmena}/bin/colmena" ] (
|
||||||
|
builtins.readFile ./colmena-wrapper.bash
|
||||||
|
);
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.deploy.enable = lib.mkEnableOption "deploy";
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = config.ssh.enable;
|
||||||
|
message = "'home/modules/deploy' requires 'home/modules/ssh'";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
home.packages = [ colmenaWrapped ];
|
||||||
|
programs.ssh.settings = lib.genAttrs hostsWithKeys (
|
||||||
|
hostname:
|
||||||
|
let
|
||||||
|
meta = myUtils.hostMeta (hostDir + "/${hostname}");
|
||||||
|
isLocal = builtins.elem "local" meta.tags;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
User = meta.host.username;
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (!isLocal) {
|
||||||
|
HostName = hostname;
|
||||||
|
ControlPath = "~/.ssh/socket-%r@%h:%p";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,17 +2,11 @@
|
|||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
myUtils,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.ssh;
|
cfg = config.ssh;
|
||||||
hostDir = ../../hosts;
|
|
||||||
hostNames = myUtils.dirNames hostDir;
|
|
||||||
hostsWithKeys = lib.filter (
|
|
||||||
hostname: builtins.pathExists (hostDir + "/${hostname}/ssh_host.pub")
|
|
||||||
) hostNames;
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.ssh.enable = lib.mkEnableOption "ssh";
|
options.ssh.enable = lib.mkEnableOption "ssh";
|
||||||
@@ -24,21 +18,7 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
enableDefaultConfig = false;
|
enableDefaultConfig = false;
|
||||||
|
|
||||||
settings =
|
settings = {
|
||||||
lib.genAttrs hostsWithKeys (
|
|
||||||
hostname:
|
|
||||||
let
|
|
||||||
meta = myUtils.hostMeta (hostDir + "/${hostname}");
|
|
||||||
isLocal = builtins.elem "local" meta.tags;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
User = meta.host.username;
|
|
||||||
}
|
|
||||||
// lib.optionalAttrs (!isLocal) {
|
|
||||||
HostName = hostname;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// {
|
|
||||||
"*" = {
|
"*" = {
|
||||||
AddKeysToAgent = "yes";
|
AddKeysToAgent = "yes";
|
||||||
ForwardAgent = false;
|
ForwardAgent = false;
|
||||||
|
|||||||
@@ -99,10 +99,6 @@ in
|
|||||||
|
|
||||||
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
inputs.colmena.packages.${pkgs.stdenv.hostPlatform.system}.colmena
|
|
||||||
];
|
|
||||||
|
|
||||||
services.locate = {
|
services.locate = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.plocate;
|
package = pkgs.plocate;
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
inputs.colmena.packages.${pkgs.stdenv.hostPlatform.system}.colmena
|
|
||||||
(pkgs.writeShellApplication {
|
(pkgs.writeShellApplication {
|
||||||
name = "wol-andromache";
|
name = "wol-andromache";
|
||||||
runtimeInputs = [ pkgs.wakeonlan ];
|
runtimeInputs = [ pkgs.wakeonlan ];
|
||||||
|
|||||||
Reference in New Issue
Block a user