Add secureboot to nutty-noon

This commit is contained in:
Charlotte 🦝 Delenk 2022-04-20 08:35:17 +01:00
parent f933e71b0e
commit b39963939c
Signed by: darkkirb
GPG key ID: AB2BD8DAF2E37122
31 changed files with 683 additions and 26 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@ result
*.qcow2
*.fd
.direnv
/efi/secret

View file

@ -9,6 +9,7 @@
#./services/tpm2.nix
./services/hydra.nix
./server.nix
./secureboot.nix
nixos-hardware.nixosModules.common-cpu-amd
nixos-hardware.nixosModules.common-gpu-amd
nixos-hardware.nixosModules.common-pc-ssd

15
config/secureboot.nix Normal file
View file

@ -0,0 +1,15 @@
{ config, ... }: {
imports = [
../modules/systemd-secure-boot
];
sops.secrets."secureboot/DB.key" = { };
boot.loader.systemd-boot = {
editor = false;
secureBoot = {
enable = true;
keyPath = config.sops.secrets."secureboot/DB.key".path;
certPath = builtins.toString ../efi/DB.crt;
};
};
}

BIN
efi/DB.auth Normal file

Binary file not shown.

BIN
efi/DB.cer Normal file

Binary file not shown.

30
efi/DB.crt Normal file
View file

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFKzCCAxOgAwIBAgIUI3+y+fO534cN62xuXIJV9MFe9IkwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaY2hpci5ycyBFRkkgU2VjdXJlIEJvb3QgREIwHhcNMjIw
NDIwMDcwNDU4WhcNMzIwNDE3MDcwNDU4WjAlMSMwIQYDVQQDDBpjaGlyLnJzIEVG
SSBTZWN1cmUgQm9vdCBEQjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AK68ldtCl7Wv9Np6+5TvrdxI3DYOk5U/GH0WEYT0JNN1PyNBnAUnGcv0Fll/Y1BV
VUcP6LnubHLqh6OuQHQpCyuUigTXPMk/84iMAqYbPCn82iym5bA6OtBcFtrY+ntu
UHBQvyerEFUHibYW112lw1VXNHS/o/PZP7CLMc9oFm9To+seavspGL0XpOOpcxn/
psDGlSAuVK9/udvMVysCpXs7KO2a6/m3mGzC4wUzXMGClzEc9wY7FAgGJxk85Ndy
RRl3ivQg67I7x+dXz17JD1HZiWMzSdvoh2EgZcmq3VPIJIBkOOjJi6WBREeIxADM
M51anY4N1luD+7fxCEumaZY2oDX7RX9mtN2jD64PX8ERn5h1GOicsbj051QJc5Gf
moR2tXbnr7cZ9f3JpADSGwkleEL0E+STOCajnXlz+QGXetF8kuL7DmS+62SeusaE
lv016QH0Q1Onj0HzqZBtqPGkOX02heGyXez6BysZBTTYhuIoO06k0EG4uzqHCGVo
UmAK8EfYw2OJASP3zKY78Hjr5MSYqZUIS2RLIqw1ujb1cJCvuEbOeM8mBTRaJmAx
w+IX57+cABmSyCgR/qrmDOw7uh69/fAlPR5jjJNUIHGjuqJL856MaFo/OQPm85vI
tPZfhICVgAXqlj1/5yLKAZqBGx3w2CGoJnp+JpyIITM1AgMBAAGjUzBRMB0GA1Ud
DgQWBBTwqZ3TzsEKqqKzKGE0fCn67vB3lzAfBgNVHSMEGDAWgBTwqZ3TzsEKqqKz
KGE0fCn67vB3lzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCR
PE3EYnPmpGufxQEQqhDqkRVPp5pzVaBbVU1Vo9WF+kJAVnJlh0DpFCwMXEuzn/1u
rtnGCH46p96cxIWrJoIWnuwAuvcTLMvwrkKLHExpPisXJR3ufrYeVhEyBd39Lrl1
FD9g6FDPMJshsRPhXZLhUKLli6IAbwji2TplCBH4SFDxHoaEezhl9J8tIlQZ6roY
L0A30qoLRQ8Z19DJ3Vhj18S5ibTgRWWcquq9pF3Swv/v4gS6cULPbGSqsnFwqVND
b3pzCdBmRKciGEXGK11nGhd++wcirxJN+U0brl8mHsP7vhX0ztBBxXkfyzo9q7cl
U3p/ae1pCzX0NyRPlYyYMZTs0ZSllDLoztrEGIp16bANOHmfh+f1xszAUYHImVl7
Zq8J3js/UtnQJD5KZIEQCleXChMJaH6qbtJKpvgqO3RsHQRJDyunNgE7r6bPTfh4
TP7DhL1QWAgTdfRPzm1qofRwnTRLitRovMDWI/95lLlPbbNL/zGkBqvur3T8fLKx
gE/X7BGHGbARc8tSDdQ81kVOchL8yvud7CMgPOdazP6dmJxcO52S9Q9KpFW/duJi
V9V33wKQe5MCbTV4jtbE6ot245vslGjB/fLt4eP6Hngth0YzbU14FhXXjJofhDq7
hB0zS9HklS54OtQBvAd7EX732UfEWZi1jeR0LL6rhA==
-----END CERTIFICATE-----

BIN
efi/DB.esl Normal file

Binary file not shown.

BIN
efi/KEK.auth Normal file

Binary file not shown.

BIN
efi/KEK.cer Normal file

Binary file not shown.

30
efi/KEK.crt Normal file
View file

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFLTCCAxWgAwIBAgIUCg57ZgQLP349Iz6LjZfplKAh+ZQwDQYJKoZIhvcNAQEL
BQAwJjEkMCIGA1UEAwwbY2hpci5ycyBFRkkgU2VjdXJlIEJvb3QgS0VLMB4XDTIy
MDQyMDA3MDQ1OFoXDTMyMDQxNzA3MDQ1OFowJjEkMCIGA1UEAwwbY2hpci5ycyBF
RkkgU2VjdXJlIEJvb3QgS0VLMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA7JvQgi2sXsEksXaCd6bQIISP9aBa0dsHUa4ZTCyyGqlOdb3GuZembdnKzjwT
AffWSoaSVUo+GeewHFOOhQNMsAEW/Vm6yNdsyOHKQxgd86WIs4AoD0ngwqx3f9yp
iwjiG6PCW6BroaVAnczOfXXwUdOWNPKCfpZd9YVZ4O6Eiq1rnuWiMtGrpNm20QMJ
oXw/ieTyJzE8GWRoQm3Sk0qfpO0ae8oVkAzeHd78yT+7ciStw+EgXFbwwjTQNovo
C0pAJABP+GFDGcv7mndMJO6Ln6tNZbuZfnboTPr34akxzgLWljplyRhn1l8e9gPy
LH1Yz3SwDq5Hm02k1ZxzF0R7S0gdcLRCmU31FISHE691SBWqMR0subDXfY3aW4Dx
oO9D+JEL1hECtPK4cEBXPIuEUnHofY3kVD820xM5DLVjJCLdyV18HoW/a3FG1fqu
SQvH1EcfLjLwJIY/OJrYiChLFM9wRDuRO7oOjOYIcNg82O7Z3tJi/fjnAXtm66Pm
rLJaMNtTKZj5GIMKQq1Opi38P9yxFhZJHk1CdV5D2L4VCz6EcFLqmoBsAF/njn9f
lQ+2No4o3sDlZEZTjJug7cgMvxqoAIIis7mRKbtUKK6Vk9wjuPLC39gRUNWkVaBs
R9OVgSVz6B1AXbbvfgc5UVVpisdAV2sKvree2Lo7TTb/k18CAwEAAaNTMFEwHQYD
VR0OBBYEFLa0KcdYrySJRfHROjG/1hCqQucZMB8GA1UdIwQYMBaAFLa0KcdYrySJ
RfHROjG/1hCqQucZMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIB
AKXBgO9HRvGqJZ83c9oOanlOM77bs9r6glfoCNRUpSzV4yqzbDUR+PToObk6RowF
hR4uHTBOYtCRAUwE/GlzlAE/pU4fteT8FsZMQq1NxfOYk6xBG8rc5hjtjlplfzZi
TW0xomOjIzzfR4CNKG9OQajGUCHquto1jAikDYSGXTWcaLYgMfqjU0WttIqRckRH
ebW1QOYQN388pirxXS2VTGir6NRNtGE+RWiXF52GveUG1zWKzEHRH3u0W3Wr25oZ
rHCEZLx69BqI/xdZJ9HM8G8U37HiZswbo2M8H6c3MrRVADksK/oTfoi9xvJl1Unr
5O4tIzSqKf0A+ZIxxa/Wg4bu+EVKds7IYb6V5hojIFznydeVWjdlHA++JsWsg2ff
XKhjRrc3TJRQ4FlQWWTXk0j+l3DVIP8UmhICeQwomj27bZ3nDhKwXZW3AliYPK2z
5jlpasnMvpyKuP2rIZlIamUsX0Fp0pjrxKPHiof0dVX1eEmM2OMVbhnjwNttDApJ
C4v/MIRfn0cPMLCXwW4FQqepPekTGUmOVzJKYFuTtdorfSSuvjtqI7iX7//MIey8
bqxU6vOzY3R00WrLhzxtb/JJ6PQ/tCuoQ2UUEzOnnhqpCXVpfHtgn34pwpfQ+Shb
cBTrK6A68XlxHGudT59lOejkIgSeo30MFQTUGtFoJG+d
-----END CERTIFICATE-----

BIN
efi/KEK.esl Normal file

Binary file not shown.

BIN
efi/MS_UEFI_db.esl Normal file

Binary file not shown.

BIN
efi/MS_Win_db.esl Normal file

Binary file not shown.

BIN
efi/MS_db.esl Normal file

Binary file not shown.

BIN
efi/PK.auth Normal file

Binary file not shown.

BIN
efi/PK.cer Normal file

Binary file not shown.

30
efi/PK.crt Normal file
View file

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFKzCCAxOgAwIBAgIUShalNDCVi3u1Xuh2w5fUYRaCy7kwDQYJKoZIhvcNAQEL
BQAwJTEjMCEGA1UEAwwaY2hpci5ycyBFRkkgU2VjdXJlIEJvb3QgUEswHhcNMjIw
NDIwMDcwNDU3WhcNMzIwNDE3MDcwNDU3WjAlMSMwIQYDVQQDDBpjaGlyLnJzIEVG
SSBTZWN1cmUgQm9vdCBQSzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AJovNh67rN+Kfj7jQ8TbguMQVwxxoHsznP0s/V07n8WCqAeiJiVJHcHC3Lb0QhVQ
oNRPbfVFG+ZxUyG+R8udnkUYGAxs6hFGFTiTlachJKah8cHl1Pj2EcAOOi31IjGV
4yxm4jxDWb5F4tBhtJSPeVida1YujjfGrbsdfvG3sK4vL9sfyhN27k08PQko313s
cB5OXgU2XJmd1yLHlHiBxQ2n6xZQ42V8Z3l5XRBHbd6p2vxPYQB/D42G99tkpTs/
dMvrLPrWgSCOX/crU8ACM0LydCBOcEa66mG5x2/GcAtrapXe/PssIqhnJOPUYbgk
T/cYHnWVfb5yCrLWxdTIvnIyMzg8wVn4fOeWZxgaqjPIhtV/7x41r6zJc7UHvtGR
yhsEtc9hzibjmrnKBie4BDWUk7zR30fMz4YZv38oQKBpzknFosS3Uzerlhx33JgA
rYxJiiURiOaIPPN+C9WmYtQr50DLAMALj0yAzVNb1au+tmNqGQEvRdZxHhCtS0HR
S8T6tJW5cZ+Uf3X0akColgoYy8pe3QolNuB6j4kGfJLu6mmQoroMkFq0qyl8i6Pu
JhaUtMMd/F01cYpNYNJYIU/DK1G06a90NP7WHq+sU3+73fzb68od3gFyvs39CHCG
BiVhMt/BJnYLN62TMYj6HLVm/L5kAWFqjzCv/5JszXorAgMBAAGjUzBRMB0GA1Ud
DgQWBBTnW/GkNs3TfMLOo/tv5dTBQssnTDAfBgNVHSMEGDAWgBTnW/GkNs3TfMLO
o/tv5dTBQssnTDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQBs
cw4zlwfLJCQAq44V7LoAYE8xuoQxiTeY90D2yAaaXlhX+UxmOXa39Vg3bB6ia3oP
kTuLmI55WMg8IkXiwNIpCJcmFqZ2wMjfzmdvn2zeDWDK/aGM80xcpytunhxDe5Fs
dy2CnBveUf1fdhyogV4bo+/CvLqJjH3oSSuDVC8SqTUHS2T/1GNgLsRIHHVpPzDq
7+vtTe2F7uEA003u+UhLy9zTwcb/kkRPeDRmzJioBcPuL5neH7wibbSQ2yLmZjGq
D9rjCjXy5iWR678wfW1c7eP3ZVBTRVbZPgNfeXNg8rVFrEC9eZGYrx0FONzBq8zc
d8BHfunjiKvLJYtA5llc+RE+CeRug9SRxfpyevU36nULKM0t1fJdKmaG7sC2LesI
4LdLen5q07Py1xfbDf35fODmw8GR0rCv6kJNqqOgjRJiuLFTySPR6QHz0P5D79Uk
T3tBurtyVNu7S+jwhUvEcpHK78hfjN0zqQD7q46n56JkaCfOA9IT0a4Qw3rzQuu4
h5su9/to2hAe2W58kzO7SatLPT8fgx1L/3z5Ne+DvLeNy8Qa1lKo/2GRL3Md6pj8
Q8sAI4WHuof8Fyj1TGLFqJeRelaiMaz+dyG0AICBrsQlNBbhV99ZgZ3GApS+y7wg
vqA7lksMrNOoB/59UIPs97u+OulNx4MXqtQTCGPaNQ==
-----END CERTIFICATE-----

BIN
efi/PK.esl Normal file

Binary file not shown.

BIN
efi/add_MS_db.auth Normal file

Binary file not shown.

44
efi/mkkeys.sh Executable file
View file

@ -0,0 +1,44 @@
#!/bin/bash
# Copyright (c) 2015 by Roderick W. Smith
# Licensed under the terms of the GPL v3
echo -n "Enter a Common Name to embed in the keys: "
read NAME
openssl req -new -x509 -newkey rsa:4096 -subj "/CN=$NAME PK/" -keyout PK.key \
-out PK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:4096 -subj "/CN=$NAME KEK/" -keyout KEK.key \
-out KEK.crt -days 3650 -nodes -sha256
openssl req -new -x509 -newkey rsa:4096 -subj "/CN=$NAME DB/" -keyout DB.key \
-out DB.crt -days 3650 -nodes -sha256
openssl x509 -in PK.crt -out PK.cer -outform DER
openssl x509 -in KEK.crt -out KEK.cer -outform DER
openssl x509 -in DB.crt -out DB.cer -outform DER
GUID=`python3 -c 'import uuid; print(str(uuid.uuid1()))'`
echo $GUID > myGUID.txt
cert-to-efi-sig-list -g $GUID PK.crt PK.esl
cert-to-efi-sig-list -g $GUID KEK.crt KEK.esl
cert-to-efi-sig-list -g $GUID DB.crt DB.esl
rm -f noPK.esl
touch noPK.esl
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt PK PK.esl PK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt PK noPK.esl noPK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k PK.key -c PK.crt KEK KEK.esl KEK.auth
sign-efi-sig-list -t "$(date --date='1 second' +'%Y-%m-%d %H:%M:%S')" \
-k KEK.key -c KEK.crt db DB.esl DB.auth
chmod 0600 *.key
echo ""
echo ""
echo "For use with KeyTool, copy the *.auth and *.esl files to a FAT USB"
echo "flash drive or to your EFI System Partition (ESP)."
echo "For use with most UEFIs' built-in key managers, copy the *.cer files;"
echo "but some UEFIs require the *.auth files."
echo ""

1
efi/myGUID.txt Normal file
View file

@ -0,0 +1 @@
30db5e21-c078-11ec-be5b-00d861d0de1e

BIN
efi/noPK.auth Normal file

Binary file not shown.

0
efi/noPK.esl Normal file
View file

BIN
efi/old_KEK.esl Normal file

Binary file not shown.

BIN
efi/old_PK.esl Normal file

Binary file not shown.

BIN
efi/old_db.esl Normal file

Binary file not shown.

BIN
efi/old_dbx.esl Normal file

Binary file not shown.

View file

@ -0,0 +1,3 @@
# Systemd Secure Boot
Taken from https://github.com/frogamic/nix-machines/tree/main/modules/systemd-secure-boot

View file

@ -0,0 +1,200 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.boot.loader.systemd-boot;
efi = config.boot.loader.efi;
systemdBootBuilder = pkgs.substituteAll {
src = ./systemd-boot-builder.py;
isExecutable = true;
inherit (pkgs) python3 sbsigntool;
binutils = pkgs.binutils-unwrapped;
systemd = config.systemd.package;
nix = config.nix.package.out;
timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else "";
editor = if cfg.editor then "True" else "False";
configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;
inherit (cfg) consoleMode;
inherit (cfg.secureBoot) keyPath certPath;
secureBootEnable = cfg.secureBoot.enable;
inherit (efi) efiSysMountPoint canTouchEfiVariables;
memtest86 = if cfg.memtest86.enable then pkgs.memtest86-efi else "";
};
checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot"
{
nativeBuildInputs = [ pkgs.mypy ];
} ''
install -m755 ${systemdBootBuilder} $out
mypy \
--no-implicit-optional \
--disallow-untyped-calls \
--disallow-untyped-defs \
$out
'';
in
{
disabledModules = [ "system/boot/loader/systemd-boot/systemd-boot.nix" ];
imports =
[
(mkRenamedOptionModule [ "boot" "loader" "gummiboot" "enable" ] [ "boot" "loader" "systemd-boot" "enable" ])
];
options.boot.loader.systemd-boot = {
enable = mkOption {
default = false;
type = types.bool;
description = "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager";
};
editor = mkOption {
default = true;
type = types.bool;
description = ''
Whether to allow editing the kernel command-line before
boot. It is recommended to set this to false, as it allows
gaining root access by passing init=/bin/sh as a kernel
parameter. However, it is enabled by default for backwards
compatibility.
'';
};
configurationLimit = mkOption {
default = null;
example = 120;
type = types.nullOr types.int;
description = ''
Maximum number of latest generations in the boot menu.
Useful to prevent boot partition running out of disk space.
<literal>null</literal> means no limit i.e. all generations
that were not garbage collected yet.
'';
};
consoleMode = mkOption {
default = "keep";
type = types.enum [ "0" "1" "2" "auto" "max" "keep" ];
description = ''
The resolution of the console. The following values are valid:
<itemizedlist>
<listitem><para>
<literal>"0"</literal>: Standard UEFI 80x25 mode
</para></listitem>
<listitem><para>
<literal>"1"</literal>: 80x50 mode, not supported by all devices
</para></listitem>
<listitem><para>
<literal>"2"</literal>: The first non-standard mode provided by the device firmware, if any
</para></listitem>
<listitem><para>
<literal>"auto"</literal>: Pick a suitable mode automatically using heuristics
</para></listitem>
<listitem><para>
<literal>"max"</literal>: Pick the highest-numbered available mode
</para></listitem>
<listitem><para>
<literal>"keep"</literal>: Keep the mode selected by firmware (the default)
</para></listitem>
</itemizedlist>
'';
};
memtest86 = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Make MemTest86 available from the systemd-boot menu. MemTest86 is a
program for testing memory. MemTest86 is an unfree program, so
this requires <literal>allowUnfree</literal> to be set to
<literal>true</literal>.
'';
};
};
secureBoot = {
enable = mkOption {
default = false;
type = types.bool;
description = "Whether to enable secureboot for systemd-boot";
};
keyPath = mkOption {
default = null;
type = types.nullOr types.str;
description = "Path to the secureboot signing key";
};
certPath = mkOption {
default = null;
type = types.nullOr types.str;
description = "Path to the secureboot signing certificate";
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !(cfg.secureBoot.enable && isNull cfg.secureBoot.keyPath);
message = "The secureboot signing key must be provided";
}
{
assertion = !(cfg.secureBoot.enable && isNull cfg.secureBoot.certPath);
message = "The secureboot signing certificate must be provided";
}
{
assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
message = "This kernel does not support the EFI boot stub";
}
];
boot.loader.grub.enable = mkDefault false;
boot.loader.supportsInitrdSecrets = true;
system = {
build.installBootLoader = checkedSystemdBootBuilder;
boot.loader.id = "systemd-boot";
requiredKernelConfig = with config.lib.kernelConfig; [
(isYes "EFI_STUB")
];
};
};
}

View file

@ -0,0 +1,300 @@
#! @python3@/bin/python3 -B
import argparse
import shutil
import os
import sys
import errno
import subprocess
import glob
import tempfile
import errno
import warnings
import ctypes
libc = ctypes.CDLL("libc.so.6")
import re
import datetime
import glob
import os.path
from typing import Tuple, List, Optional, Callable
def install_signed_if_required(source: Callable[[], str], dest: str) -> None:
if "@secureBootEnable@" == "1":
try:
subprocess.check_call(
["@sbsigntool@/bin/sbverify", "--cert=@certPath@", dest],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
subprocess.check_call([
"@sbsigntool@/bin/sbsign",
"--key=@keyPath@",
"--cert=@certPath@",
"--output=%s.tmp" % (dest),
source()],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
os.rename("%s.tmp" % (dest), dest)
elif not os.path.exists(dest):
shutil.copy(source(), dest)
def efi_section(name: str, path: str, vma: str) -> List[str]:
return [
"--add-section",
".%s=%s" % (name, path),
"--change-section-vma",
".%s=%s" % (name, vma)]
def system_dir(profile: Optional[str], generation: int) -> str:
if profile:
return "/nix/var/nix/profiles/system-profiles/%s-%d-link" % (profile, generation)
else:
return "/nix/var/nix/profiles/system-%d-link" % (generation)
# The boot loader entry for memtest86.
#
# TODO: This is hard-coded to use the 64-bit EFI app, but it could probably
# be updated to use the 32-bit EFI app on 32-bit systems. The 32-bit EFI
# app filename is BOOTIA32.efi.
MEMTEST_BOOT_ENTRY = """title MemTest86
efi /efi/memtest86/BOOTX64.efi
"""
def write_loader_conf(profile: Optional[str], generation: int) -> None:
with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f:
if "@timeout@" != "":
f.write("timeout @timeout@\n")
if profile:
f.write("default nixos-%s-generation-%d.efi\n" % (profile, generation))
else:
f.write("default nixos-generation-%d.efi\n" % (generation))
if not @editor@:
f.write("editor 0\n");
f.write("console-mode @consoleMode@\n");
os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
def profile_path(profile: Optional[str], generation: int, name: str) -> str:
return os.path.realpath("%s/%s" % (system_dir(profile, generation), name))
def path_from_profile(profile: Optional[str], generation: int, name: str) -> str:
store_file_path = profile_path(profile, generation, name)
suffix = os.path.basename(store_file_path)
store_dir = os.path.basename(os.path.dirname(store_file_path))
efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
return efi_file_path
def describe_generation(generation_dir: str) -> str:
try:
with open("%s/nixos-version" % generation_dir) as f:
nixos_version = f.read()
except IOError:
nixos_version = "Unknown"
kernel_dir = os.path.dirname(os.path.realpath("%s/kernel" % generation_dir))
module_dir = glob.glob("%s/lib/modules/*" % kernel_dir)[0]
kernel_version = os.path.basename(module_dir)
build_time = int(os.path.getctime(generation_dir))
build_date = datetime.datetime.fromtimestamp(build_time).strftime('%F')
description = "NixOS {}, Linux Kernel {}, Built on {}".format(
nixos_version, kernel_version, build_date
)
return description
def write_entry(profile: Optional[str], generation: int) -> None:
if profile:
entry_file = "@efiSysMountPoint@/EFI/Linux/nixos-%s-generation-%d.efi" % (profile, generation)
else:
entry_file = "@efiSysMountPoint@/EFI/Linux/nixos-generation-%d.efi" % (generation)
with tempfile.TemporaryDirectory() as tmpdir:
def make_unified_kernel() -> str:
kernel = profile_path(profile, generation, "kernel")
initrd = profile_path(profile, generation, "initrd")
osrel = profile_path(profile, generation, "etc/os-release")
cmdline = "%s/cmdline" % (tmpdir)
efistub = profile_path(profile, generation, "sw/lib/systemd/boot/efi/linuxx64.efi.stub")
if not os.path.exists(efistub):
efistub = "@systemd@lib/systemd/boot/efi/linuxx64.efi.stub"
try:
append_initrd_secrets = profile_path(profile, generation, "append-initrd-secrets")
subprocess.check_call([append_initrd_secrets, initrd])
except FileNotFoundError:
pass
generation_dir = os.readlink(system_dir(profile, generation))
kernel_params = "init=%s/init " % generation_dir
with open("%s/kernel-params" % (generation_dir)) as params_file:
kernel_params = kernel_params + params_file.read()
with open(cmdline, 'w') as f:
f.write(kernel_params)
subprocess.check_call([
"@binutils@/bin/objcopy",
*efi_section("osrel", osrel, "0x20000"),
*efi_section("cmdline", cmdline, "0x30000"),
*efi_section("linux", kernel, "0x40000"),
*efi_section("initrd", initrd, "0x3000000"),
efistub,
"%s/unified.efi" % (tmpdir)])
return "%s/unified.efi" % (tmpdir)
install_signed_if_required(make_unified_kernel, entry_file)
def mkdir_p(path: str) -> None:
try:
os.makedirs(path)
except OSError as e:
if e.errno != errno.EEXIST or not os.path.isdir(path):
raise
def get_generations(profile: Optional[str] = None) -> List[Tuple[Optional[str], int]]:
gen_list = subprocess.check_output([
"@nix@/bin/nix-env",
"--list-generations",
"-p",
"/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"),
"--option", "build-users-group", ""],
universal_newlines=True)
gen_lines = gen_list.split('\n')
gen_lines.pop()
configurationLimit = @configurationLimit@
return [ (profile, int(line.split()[0])) for line in gen_lines ][-configurationLimit:]
def remove_old_entries(gens: List[Tuple[Optional[str], int]]) -> None:
rex_profile = re.compile("^@efiSysMountPoint@/EFI/Linux/nixos-(.*)-generation-.*\.efi$")
rex_generation = re.compile("^@efiSysMountPoint@/EFI/Linux/nixos.*-generation-(.*)\.efi$")
known_paths = []
for gen in gens:
known_paths.append(path_from_profile(*gen, "kernel"))
known_paths.append(path_from_profile(*gen, "initrd"))
for path in glob.iglob("@efiSysMountPoint@/EFI/Linux/nixos*-generation-[1-9]*.efi"):
try:
if rex_profile.match(path):
prof = rex_profile.sub(r"\1", path)
else:
prof = "system"
gen_number = int(rex_generation.sub(r"\1", path))
if not (prof, gen_number) in gens:
os.unlink(path)
except ValueError:
pass
for path in glob.iglob("@efiSysMountPoint@/EFI/Linux/*"):
if not path in known_paths and not os.path.isdir(path):
os.unlink(path)
def get_profiles() -> List[str]:
if os.path.isdir("/nix/var/nix/profiles/system-profiles/"):
return [x
for x in os.listdir("/nix/var/nix/profiles/system-profiles/")
if not x.endswith("-link")]
else:
return []
def main() -> None:
parser = argparse.ArgumentParser(description='Update NixOS-related systemd-boot files')
parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default NixOS config to boot')
args = parser.parse_args()
try:
with open("/etc/machine-id") as machine_file:
machine_id = machine_file.readlines()[0]
except IOError as e:
if e.errno != errno.ENOENT:
raise
# Since systemd version 232 a machine ID is required and it might not
# be there on newly installed systems, so let's generate one so that
# bootctl can find it and we can also pass it to write_entry() later.
cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"]
machine_id = subprocess.run(
cmd, text=True, check=True, stdout=subprocess.PIPE
).stdout.rstrip()
if os.getenv("NIXOS_INSTALL_GRUB") == "1":
warnings.warn("NIXOS_INSTALL_GRUB env var deprecated, use NIXOS_INSTALL_BOOTLOADER", DeprecationWarning)
os.environ["NIXOS_INSTALL_BOOTLOADER"] = "1"
if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
# bootctl uses fopen() with modes "wxe" and fails if the file exists.
if os.path.exists("@efiSysMountPoint@/loader/loader.conf"):
os.unlink("@efiSysMountPoint@/loader/loader.conf")
if "@canTouchEfiVariables@" == "1":
subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "install"])
else:
subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "--no-variables", "install"])
else:
# Update bootloader to latest if needed
systemd_version = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[1]
sdboot_status = subprocess.check_output(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "status"], universal_newlines=True)
# See status_binaries() in systemd bootctl.c for code which generates this
m = re.search("^\W+File:.*/EFI/(BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$",
sdboot_status, re.IGNORECASE | re.MULTILINE)
if m is None:
print("could not find any previously installed systemd-boot")
else:
sdboot_version = m.group(2)
if systemd_version > sdboot_version:
print("updating systemd-boot from %s to %s" % (sdboot_version, systemd_version))
subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "update"])
install_signed_if_required(lambda: "@systemd@/lib/systemd/boot/efi/systemd-bootx64.efi", "@efiSysMountPoint@/EFI/BOOT/BOOTX64.efi")
install_signed_if_required(lambda: "@systemd@/lib/systemd/boot/efi/systemd-bootx64.efi", "@efiSysMountPoint@/EFI/systemd/systemd-bootx64.efi")
mkdir_p("@efiSysMountPoint@/EFI/Linux")
mkdir_p("@efiSysMountPoint@/loader/entries")
gens = get_generations()
for profile in get_profiles():
gens += get_generations(profile)
remove_old_entries(gens)
for gen in gens:
try:
write_entry(*gen)
if os.readlink(system_dir(*gen)) == args.default_config:
write_loader_conf(*gen)
except OSError as e:
print("ignoring profile '{}' in the list of boot entries because of the following error:\n{}".format(profile, e), file=sys.stderr)
memtest_entry_file = "@efiSysMountPoint@/loader/entries/memtest86.conf"
if os.path.exists(memtest_entry_file):
os.unlink(memtest_entry_file)
shutil.rmtree("@efiSysMountPoint@/efi/memtest86", ignore_errors=True)
if "@memtest86@" != "":
mkdir_p("@efiSysMountPoint@/efi/memtest86")
for path in glob.iglob("@memtest86@/*"):
if os.path.isdir(path):
shutil.copytree(path, os.path.join("@efiSysMountPoint@/efi/memtest86", os.path.basename(path)))
else:
shutil.copy(path, "@efiSysMountPoint@/efi/memtest86/")
memtest_entry_file = "@efiSysMountPoint@/loader/entries/memtest86.conf"
memtest_entry_file_tmp_path = "%s.tmp" % memtest_entry_file
with open(memtest_entry_file_tmp_path, 'w') as f:
f.write(MEMTEST_BOOT_ENTRY)
os.rename(memtest_entry_file_tmp_path, memtest_entry_file)
# Since fat32 provides little recovery facilities after a crash,
# it can leave the system in an unbootable state, when a crash/outage
# happens shortly after an update. To decrease the likelihood of this
# event sync the efi filesystem after each update.
rc = libc.syncfs(os.open("@efiSysMountPoint@", os.O_RDONLY))
if rc != 0:
print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr)
if __name__ == '__main__':
main()

View file

@ -1,26 +1,28 @@
network:
wireguard:
privkey: ENC[AES256_GCM,data:y0yJd/IsKQIuYPnf/GKtQy8HXeS5PeiM0FMu1ZWqMOaO22LnJcWfQV9IZs8=,iv:nQNKSYgaQ+kgxiFRatMFGyJm3ui8iQ+xKIU9zOF2RiM=,tag:bDQE9cUrXAa8VuG0pMwf8g==,type:str]
privkey: ENC[AES256_GCM,data:szEM0VB3vTMs0PbquIbH/nLUOcntT0w5pggZzUEhTsUTQPbP2X20gROEK4w=,iv:T6xB0HKPtmVe/83p85y30Wwiq6bhflQCET/epa+E7PE=,tag:ThFxTCdcYXxql/bggNWIUQ==,type:str]
services:
nix-serve:
privkey: ENC[AES256_GCM,data:N2SpdR1ctfMDDQXhrQxQqGhpA7QkO3XumLFcBBUVi4MQ2VekG3NqzQfyfffxkfNB1/AJlb0KghDCde44baHCAJ6J5d3xFLluGPGp+uyLUA5qZFqqu9l6O41hOgkUu469pQ==,iv:5GZ6sPcdIdbEPMh60I5sgCqB9jhFqkwu6luxF9kpNVk=,tag:PeAJ1YEiHDFWuXYVsmGINw==,type:str]
privkey: ENC[AES256_GCM,data:H5bhEcGthmrPmnBx3npzBT4C6GXmxpedlsiG66m009Crca4Ho15SKUjUUVVRXPtcHO38zilJrh5tppWAkb34MSdLSYrVv0zHyeuJVG+LQmZ3SpUNTHHZwKFCfTYNXDxyqQ==,iv:w49+cYfTcz31jgMqTmFuRmIyybjrA6aDSdCZ+IovQVQ=,tag:7vYRLrZF7JFLhGXqAQOhiQ==,type:str]
hydra:
gitea_token: ENC[AES256_GCM,data:MMDgCQSuRPBav6YhdW2tcE0p2s24yDzACgqdm6kBrdlpaNkXxBw8ZQ==,iv:q8ye1vW4SeHzwJjf3Mfbac76dtIYid6E61nBMWj5sUg=,tag:qskumtwySnkPKof95BvnGg==,type:str]
github_token: ENC[AES256_GCM,data:FRvWJ/FXNPjfvrGEVlieHENu7UmkDHobB/PwAVVI4nZxtzGLqwljPg==,iv:8supeS0aTTRiVh1e5X4RNs9zP2UAKNF9JNZjw1geMPY=,tag:1RutlYl8soA+raxEZJT3CA==,type:str]
aws_credentials: ENC[AES256_GCM,data:zztGMpBqCaPBuSehvF9MVGScGA/OlU2H/w+WVhacMkE8QxNjQsfI69Ztc8R/tktQTNn9+7vsvJlQIIDnA/SVm2PmeKsD0GVg8wEehCGz2WBWClUZ2X5tQZsmC9h1YogND7FN7vgSWjSMvMllzGa84Q==,iv:egFR0o650ZbWELL1e3hMvU5oYTNxZTY41Kn04QHlXk8=,tag:E/zMWzcTsPbPMCFy3l5LNg==,type:str]
gitea_token: ENC[AES256_GCM,data:3Ig/5LUJuG6kt4KPOFrACIYHPaHKd8Csb6tRoDG1V+5zEWWc1f4msA==,iv:PyLk/XPmibWC8gssbWR5ah+880ZkAML/aumwkZptlgI=,tag:JXZTLZPPbhC/6tGoFTLgng==,type:str]
github_token: ENC[AES256_GCM,data:8Hl1X5w7g4JsfsGohmGL2sWdEsP7HyaJKXxqNBG0VosSoLPrDNhNRw==,iv:cX1Wp9MHeGtdxkqvag7TCqju9Pq+6L5kJVQKHRszCdE=,tag:/x25BMa19jGQ7/Lf7gI7BQ==,type:str]
aws_credentials: ENC[AES256_GCM,data:jw2iHyAAgAWyDUj9SZX3l4qFnxfdA0rNtTi7AJhfJMnq40S3ca476c+jkHb6ETEqb85C3a1k2So7gbMhHMaWnV5te7pM0JBHDm6qau3u50PdkQUxbFZXhMjjnjhYOrxANICS/AEqv3Aiui3HLH+Xeg==,iv:DS12/Oy7yzleeOoOs4n0Ratbq7k9+8+Dti/Wk59GduA=,tag:fB8MDgrg221mdo9vztIvtg==,type:str]
security:
restic:
password: ENC[AES256_GCM,data:Mr8uOq7UtN7hdWWQQbyDfdC+jzI=,iv:Ey2Q0O+UPVhrb+z54l/w6sk3OQEAvAoMyP6TfJBRgmI=,tag:4z/NvwFpEE/QE7i/lSeexQ==,type:str]
password: ENC[AES256_GCM,data:h6gOInhL3WizolCbfcYM1mC1kqA=,iv:WeAGzefn1pZobi3Zw+PS5nAXTRWJ5Xj2bbtrleI92cQ=,tag:eG/xlHRL6U5G1ZtCtqGV1Q==,type:str]
acme:
dns: ENC[AES256_GCM,data:vxGlQldRgsQErlZ5SzvjmDGth3Ozf3FIsREp7rPevzWEdeQzrX5U1f9enBed2Bdysa2SIdxtI5ZFl9Tql7YWBLFGURvjjeWEFE5FjoOznlYGqKDMUcnnoWcMa99fXpQAdYP1ThgRPZ69DAeLOopndHAl0KFQgwJFrRDlSDfj19orcJ4pXxGmw1dEY0SpiU71SfmS3oLyvdWNmnPklC46qcK4q/D+SON1Nk2J4O0rTDi6OgZv37fVz4I17P66L5XVzepwZGppGIJDd+IdIm/I55Q99kjeRj2VVw==,iv:WkGdSldoBepdtoIouIRTCfWVHmp9wYkYqUonzWo4H+o=,tag:/l7NAQxYzr8dfflsKhEUcw==,type:str]
dns2: ENC[AES256_GCM,data:j5Lmr1T1BfPKJmdf+Z9MYx7L9z/PqDTHTxYZo/osln7/wx+vlGf/1zWKq0m2AUObqIGRDqytKuOIwiGw+Q32OviEF2TwJkyLg0Dxjp5u9un8ha6FAa7YgfbHCw6W/FS8xWP/CE6ZOxgDd6e6NgPB8WcPltlsNMKtQ9gjQ6IZm4mdzfCcsjIerI6ZTkHjJHGZJSadJ8zFLeFaHosAIneDQC8ODRyDE8lSZuscIMCVhR+ynvsncSGOu7Vkh5mLPmseZjURiKrpNmxB/xkFvfdkwNnEUUJs4Da79w==,iv:yjA/yp8wOAA++hSKETPbXXOuAN0pozdgMDOFh0nNvq0=,tag:oAiiN/ol76kVEunoruAliQ==,type:str]
cloudflare: ENC[AES256_GCM,data:WfSxl91cv+1ztYN7AGLyNAX8tkF1OjAF5sndqVvZHpbWmq8IfeuVe1Xu/FNdZVUeSinyDCuf0uMBMlaE+frIdHbm/MrvnlVQ7oBmtmDKsj40P19TqLD77KqRfccX8tDsEeTsasFT/7fWab9zchHzmb7UMcb7372LwmAtGgtEW6AfF8RKKt7fEPJGWx8pcxKdUhFvrn6C0Wvg+yh0v0NpX3DNz8psx4zkd/BU9CGHBP+O5SIRbdSHYU8=,iv:5gPdKrlPCxKyq2hQqM/PzTgaT+xDpo6Czdf1BUpxlbI=,tag:S+A5XRHNDzSu2NuqR0id1A==,type:str]
dns: ENC[AES256_GCM,data:5VC6oBXZcfg3w4tX98hmvZO/Z2LK0wsHczJ4gtWGoJEu9vzMuwkLkcXkwBffL35HBXcV5NEK4X9/7TAlcM2vWNYjxBnHY3gT2763zQs9WUwQlVYLCXD3dptROUO9u0wU+Ekj/+xYY74V7WGrolzPnbrJo5/QyOcqc05N5yUu8oLCgTG9TlN7kVKUiEFBklZIolM9hoqBtYSoCQmnzigR1uc5UdLxVhAmThoJOmC9MnWym0smMqB6cTVyd27fMU4731yQSsO0Uji0BP/3LZN+S9e2/cIHI9migg==,iv:BjvU+s2mv36S89OMYGPN/c9LQEGdEIq0SMDHQys7XXU=,tag:ljhUMj8BHjRTvWYig21lgA==,type:str]
dns2: ENC[AES256_GCM,data:g1t7Eq800gSfqZkqGb36PxTqtV+Ah4R4Jitv29Be110kcRVTdfdKHELpCp/YelHbyjhf8w4JdoSOXqb2nXKGdpXQINidjOBHAYusZHJaRiGJWKqswSqnvUiWw0j0tyIaR7jn9Il25IjlQ4PhlpAV0fk/Ezm+COVKC8GlY03bscPCHFSpQCLdGzKv7Yl9Fz+K3excZGm1OZMOnjCoZnHsi/oT0TFeymXw+Lkm2v6/B8PVoOpe8N5woH92sFgbuIvAZIyeTIaRH8e9K4ZvOYAQMqY6SANnqL1Q0w==,iv:XRjXqZbcC80KnpoWUIDBinoVsJBCzM3tJ6v4qPcJYiw=,tag:VUgl3efofNwhFP/VcH1hLw==,type:str]
cloudflare: ENC[AES256_GCM,data:ONvtdegXFZuPSa/vQDZSDmQ4x8H1Qz08/i6NE6p7eJQav3c1rM1ai0oMdRMKZ1kfHGW99hkEGCacnIO0Lbg9iJbCQeR+Co/CM6G4UUeLgt3EOAI+J0aBpgMAjAbU7wPZVvG4mUarcwDkwx61tyKj/8gPhBMvArjnnUMyU3Xn+iuHHG5TeBRhX6KnlEwmcRDLWnYObmLcVzU37nque9NBrvyPT9iNANjF9WjtOnQU63EhFnTp4HUwcW8=,iv:DpQOKpdXOrTvxsWHJoIeEPc/rZJQmG3NprhEVqGpOgw=,tag:N47EaTXcFOAer2oL6wsDhQ==,type:str]
email:
lotte@chir.rs: ENC[AES256_GCM,data:02v6qsTC30thvqQ4yDpYhfyNVg==,iv:rdz3HHlAyyt1TR7iUXpokIlBC8VEdS0GLoCkItBc3HY=,tag:/aNoPNoMeVGCWRT3j+F+ew==,type:str]
mdelenk@hs-mittweida.de: ENC[AES256_GCM,data:rXwwhdX2STqJjO2UMqW9YeXc8JtJ2DXLptZvVN9552ldRgZU7OoNiPxbYg/Kr7ZOkl/8HIg0yFa1uQIbvQxuoQ==,iv:ThZzE7m05FS1NPH/mvWF/vflxC4pmZCMX12iOUzKQfQ=,tag:qK0+ZA8486YgaW/I7BrfPQ==,type:str]
lotte@chir.rs: ENC[AES256_GCM,data:P6zZjE5iqqPifjwXxFtXwNFnKg==,iv:m9bCuByKC+ppg/+K97hwkuO29bdtID8bFW4Ie7hwAgA=,tag:2sNxBWery9H+Mu5hzJPELA==,type:str]
mdelenk@hs-mittweida.de: ENC[AES256_GCM,data:gxUiw3kGER3g6MLfy6xDWQhsWnkvvmn68bBYaXf7JTs7/KPSv25kQatziMvotjkkSDmjqP0/r5fqlVLI+QnPUQ==,iv:Ry4LhbEhgI6J61nQ6bGhu1Y8ZLSdAP3rEAMbI/h0j7c=,tag:woIMx78b8Fb2yvsBznwOZw==,type:str]
password:
root: ENC[AES256_GCM,data:PioiMzGCro6vLXfCkJrmWN+SZkwdCSiHUXMmmtK3FLl2XRhtYFixeiDI+YFNT3SmdJ3clI4R+IrEV2pMjXX/jHejI80NnTzJU32Rp2/dYxwz6qk9vNwRC1OiyrsF67sg4VoSBVinbGMVuA==,iv:bIRfmXQqdv+PJDpHtnjR5RJUd5E5HC+Q+kN1ncRdUMU=,tag:6PAVQ1DPh5+5BrWkZrp4MQ==,type:str]
darkkirb: ENC[AES256_GCM,data:b1yBZSqsJh3Er8/U7dLa3L92uoe3/MH2xoOK+eOAjNYAL5kJD2yf+5ikTR9N+bRsVTjoGxfcpYfbx08WSK9NY8lP0u7zdQ32g9gyWSlpZiFjm9yQd3iwdxbnrtNYMd6fmSqTaPOAQqW9ww==,iv:Q1nuM+lmpqAJgiBKISjOKdLjTRJD3YFjaSDR6j1e43w=,tag:2O/3LY9hIvcixGtshWVDDg==,type:str]
root: ENC[AES256_GCM,data:EqOfup6j6v1rhfjN5/zKTgN/QuUVytSeed3F0Tj+lWRtPUCmP1CdXTt7dzU98LA88S48M9C4c2sQ3SsJnfoApvZ2VGfMeoMo40mdEaSJR4FJo5KxR+OYjsc2BD0CGnAWBUXL8OnXB6HfYA==,iv:gXqTmyCX7OBUSBAsjDee0qG+wwkENCNtZ7VU89WQQFI=,tag:aNNJI93+L4NxV1kyW6lqUw==,type:str]
darkkirb: ENC[AES256_GCM,data:iTWDpNIMlh6DMSBn981M5QTYk0uFutM1i7J2aiLWILIp8yqIJgufMTNndlNPeM0CMjxAaER392f9z9pyTpxQ27JdSBO79AOflqfk280NBICMkVSOUQSEdnGaA5JLfgS4TwNbO1cSCPNlxg==,iv:ItjvSYOm68ZjjkspgVF2s4J4saq7+TIBbEaIhlQpnaI=,tag:KBlXqLL2Fh6IrSQn4Fq0mQ==,type:str]
secureboot:
DB.key: ENC[AES256_GCM,data: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,iv:OFiuSu/Kh8mf4BxwtbpT4TH4oDS+YXu0GFK5/Zy+C/w=,tag:Fu4WeWJhWwQGFLB1c3qyXg==,type:str]
sops:
kms: []
gcp_kms: []
@ -30,24 +32,24 @@ sops:
- recipient: age1wfftrnyngg7nxcwvt7m590fwx3w7p4kkrjn9uprjq0u3k3ym4s3qqzkmzm
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzcDBaa0FXM21xNGErS3RH
S0gzZjJ1ZEFNTTFUZElMVFErVU0vbmVqQ0N3ClNpODB6djJ1bCtHNkVyQXFXWEpN
MkVabkJEUDJ4enVIRHFmcGlBRmsxcjgKLS0tIDBsTllyYXdsK0NLSDRKTkFRbk9P
U0JxSTR3WEFvZjVoMjJsV3NYNVFpYTAKxCpvEDbEjh3sNR+2X7AsReYPxi9n3bpP
g+IVnv+EX9CkqBNbpAHiwqzekVXNqM7SxMmgSasZ4IGRK1Wcf5NU0w==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUTnNSMC9lMmN0eVh6ck1i
Z0piR3ZnVTZNSTZCZTdGZld1b084L1RlWm40Ck9RNk0zWVVVWE5MeVo5S20ycW5W
c1J5T0JiRlNvU2t1MVlwU0FGbjRqV0EKLS0tIE9FZnVvQzEycjVnVGNJbVA1Q0li
eEtjUkdQek1peEdhbXpnSDJ6ZWlQQ00K+wUZi9x5ja1832ov4DYxCH5fbfZFkXv1
8U5idFtpxkQQH1mYKfBASC/3WbpH/xv3SCpIpqisqEVc7lL/2xD9cQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2022-04-13T14:30:15Z"
mac: ENC[AES256_GCM,data:jYbhAoFF6giESwzrWh1lpORjERoDl1sAOViYuaffslyBMc/RAH4i6LTCdtaMbqckciuxjppQeVvSXNU/TgjrASNHQNCgG4UDGZD91vx4tdLeABCCq/b65eTjUi9pNdFm5GzGaySvYzztcPxcVnqhaIY3z5N1fBQKYWu4LAJbqRM=,iv:ZqPomgoGoge/wPRdO0BEXt5Zh6VSTr2mS/iStUvLcbE=,tag:/EByxD3yAZ9MP1u+/hXo8A==,type:str]
lastmodified: "2022-04-20T07:17:38Z"
mac: ENC[AES256_GCM,data:ESjkNG8vQXJY9L2M8R/tuFtgjIR3UTIibwqTXKnU0/dxxdrr+y8jCdn2h4Yqm6BuZOCF6S4NRUsqLd1GoTORMyEENjpyhglhvld+bArZWQs/S3DDApgU5H9/gppDSVNN0XQTifsk9Wabm/ZPlfeBWKoGTbSQBKnMrX+LHiiwN/Q=,iv:5FURepA8YIps+nJMczarLdt27BQC2moSvG6qoz2+Z/o=,tag:2ikHAzF3lAiEB+uQozMOww==,type:str]
pgp:
- created_at: "2022-04-02T06:17:40Z"
- created_at: "2022-04-20T07:17:37Z"
enc: |
-----BEGIN PGP MESSAGE-----
hF4DAAAAAAAAAAASAQdACVlYZwj9xdlHrbQ/yMc8jx0Ls3LSAyWGqJiIaj1ksRsw
XvU2dbAYhSLrpJSkTU+lSmjXFmPrq7GnkNKy+bPFWu7bmghKItXpO//6AcU7IB9m
0l4BOrloU1EedUf6rJUewUtQP7nNXsJ+iqWOMpN5Y6GX4UWeXMZ8AgSEqpHni9QV
KFa5VdU73/ms2+zatFxqj1bix4ZZqsxwapWreyKgo2jwIOVLZyHAu9TyoN7rLyLP
=x+Y/
hF4DAAAAAAAAAAASAQdAxefXUpTNr2aKPMHYv7vh1VygwqEGmUF/jLnmUvhY+Akw
okJvyJqVK2Fe2t/FOxBVfmfktMQ0K7GN6aoIGrRl6BLu1hUzRHyURYquKqOpDAPt
0l4BOoRh/9iRaDICkEh0dG3OSgL7xG3L/QcNXB0K6H/tYBzfIJ0oCmCqwaG9khm8
5dqZtQ9x0Oxfdp6LfSwRk4C18n/fzDz4DydPH4IbURhXDVUu34p/Alg6kjwGYFyM
=xlEM
-----END PGP MESSAGE-----
fp: 46C6A7E14BC7812E86C2700737FE303AAC2D06CD
unencrypted_suffix: _unencrypted