deploy-host.nix
· 2.7 KiB · Nix
Raw
(writeNuBin "deploy"
{
makeWrapperArgs = [
"--prefix"
"PATH"
":"
(lib.makeBinPath (
with pkgs;
[
sops
git
ssh-to-age
nixos-anywhere
openssh
]
))
];
}
# nu
''
# A deployment script utilizing nixos-anywhere
#
# 1. Generates new SSH and Age Keys
# 2. Updates all secrets of that host
# 3. Deploys config with new Keys
@example "Deploy Zarqa" {deploy zarqa 192.168.2.201}
@example "Deploy Zarqa and generate facter.json" {deploy zarqa 192.168.2.201 --generate-hardware-report}
def main [host: string, ip: string, --generate-hardware-report]: nothing -> nothing {
# Work on raw YAML text for anchors
let raw = open --raw .sops.yaml
let anchor = $"host_($host)"
if not ($raw | str contains $"&($anchor)") {
error make $"Host anchor ($anchor) not found in .sops.yaml"
}
let old_age_key = $raw
| parse --regex $"&($anchor) \(?<key>age[0-9a-z]+\)"
| get key.0
if ($old_age_key | is-empty) {
error make $"Failed to extract old age key for ($anchor)"
}
# Create temp dir
let tempdir = mktemp -d
let ssh_dir = ($tempdir)/etc/ssh
mkdir $ssh_dir
let rsa_key = $"($ssh_dir)/ssh_host_rsa_key"
let ed25519_key = $"($ssh_dir)/ssh_host_ed25519_key"
ssh-keygen -t rsa -b 4096 -f $rsa_key -N "" -C $host
ssh-keygen -t ed25519 -f $ed25519_key -N "" -C $host
let age_pub = open --raw $"($ed25519_key).pub" | ssh-to-age
# Replace anchor in raw YAML
$raw
| str replace -r $"&($anchor) age[0-9a-z]+" $"&($anchor) ($age_pub)"
| save --force .sops.yaml
print $"Updated .sops.yaml for ($host)"
let git_files = git ls-files | lines
(open .sops.yaml).creation_rules
| where {|rule|
$rule.key_groups | any {|kg|
$kg.age | any {|k| $k == $age_pub }
}
}
| get path_regex
| par-each {|regex| $git_files | where {|f| $f =~ $regex } }
| flatten
| uniq
| each {|file_to_reencrypt|
print $"Re-encrypting ($file_to_reencrypt)"
sops updatekeys $file_to_reencrypt
print # needed so that updatekeys output is shown
}
nixos-anywhere --flake $".#($host)" (if $generate_hardware_report { $"--generate-hardware-config nixos-facter ./modules/hosts/($host)/facter.json" } else { "" }) --target-host $"root@($ip)" --extra-files $tempdir
# Remove lingering temp directory.
# We only care about the successful case because
# failing means the keys won't be used anyway
rm -rf $tempdir
}
''
)
| 1 | (writeNuBin "deploy" |
| 2 | { |
| 3 | makeWrapperArgs = [ |
| 4 | "--prefix" |
| 5 | "PATH" |
| 6 | ":" |
| 7 | (lib.makeBinPath ( |
| 8 | with pkgs; |
| 9 | [ |
| 10 | sops |
| 11 | git |
| 12 | ssh-to-age |
| 13 | nixos-anywhere |
| 14 | openssh |
| 15 | ] |
| 16 | )) |
| 17 | ]; |
| 18 | } |
| 19 | # nu |
| 20 | '' |
| 21 | # A deployment script utilizing nixos-anywhere |
| 22 | # |
| 23 | # 1. Generates new SSH and Age Keys |
| 24 | # 2. Updates all secrets of that host |
| 25 | # 3. Deploys config with new Keys |
| 26 | @example "Deploy Zarqa" {deploy zarqa 192.168.2.201} |
| 27 | @example "Deploy Zarqa and generate facter.json" {deploy zarqa 192.168.2.201 --generate-hardware-report} |
| 28 | def main [host: string, ip: string, --generate-hardware-report]: nothing -> nothing { |
| 29 | # Work on raw YAML text for anchors |
| 30 | let raw = open --raw .sops.yaml |
| 31 | |
| 32 | let anchor = $"host_($host)" |
| 33 | if not ($raw | str contains $"&($anchor)") { |
| 34 | error make $"Host anchor ($anchor) not found in .sops.yaml" |
| 35 | } |
| 36 | |
| 37 | let old_age_key = $raw |
| 38 | | parse --regex $"&($anchor) \(?<key>age[0-9a-z]+\)" |
| 39 | | get key.0 |
| 40 | if ($old_age_key | is-empty) { |
| 41 | error make $"Failed to extract old age key for ($anchor)" |
| 42 | } |
| 43 | |
| 44 | # Create temp dir |
| 45 | let tempdir = mktemp -d |
| 46 | let ssh_dir = ($tempdir)/etc/ssh |
| 47 | mkdir $ssh_dir |
| 48 | |
| 49 | let rsa_key = $"($ssh_dir)/ssh_host_rsa_key" |
| 50 | let ed25519_key = $"($ssh_dir)/ssh_host_ed25519_key" |
| 51 | |
| 52 | ssh-keygen -t rsa -b 4096 -f $rsa_key -N "" -C $host |
| 53 | ssh-keygen -t ed25519 -f $ed25519_key -N "" -C $host |
| 54 | |
| 55 | let age_pub = open --raw $"($ed25519_key).pub" | ssh-to-age |
| 56 | |
| 57 | # Replace anchor in raw YAML |
| 58 | $raw |
| 59 | | str replace -r $"&($anchor) age[0-9a-z]+" $"&($anchor) ($age_pub)" |
| 60 | | save --force .sops.yaml |
| 61 | |
| 62 | print $"Updated .sops.yaml for ($host)" |
| 63 | |
| 64 | let git_files = git ls-files | lines |
| 65 | |
| 66 | (open .sops.yaml).creation_rules |
| 67 | | where {|rule| |
| 68 | $rule.key_groups | any {|kg| |
| 69 | $kg.age | any {|k| $k == $age_pub } |
| 70 | } |
| 71 | } |
| 72 | | get path_regex |
| 73 | | par-each {|regex| $git_files | where {|f| $f =~ $regex } } |
| 74 | | flatten |
| 75 | | uniq |
| 76 | | each {|file_to_reencrypt| |
| 77 | print $"Re-encrypting ($file_to_reencrypt)" |
| 78 | sops updatekeys $file_to_reencrypt |
| 79 | print # needed so that updatekeys output is shown |
| 80 | } |
| 81 | |
| 82 | nixos-anywhere --flake $".#($host)" (if $generate_hardware_report { $"--generate-hardware-config nixos-facter ./modules/hosts/($host)/facter.json" } else { "" }) --target-host $"root@($ip)" --extra-files $tempdir |
| 83 | |
| 84 | # Remove lingering temp directory. |
| 85 | # We only care about the successful case because |
| 86 | # failing means the keys won't be used anyway |
| 87 | rm -rf $tempdir |
| 88 | } |
| 89 | '' |
| 90 | ) |