Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f9ae3f94 |
No files matched your search
Generated
+23
-23
@@ -151,11 +151,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792664877,
|
"lastModified": 1788346295,
|
||||||
"narHash": "sha256-yl7J8TPJ9GCG0T5KreuquOpyP2yzBHHaK/IlkO8x8z0=",
|
"narHash": "sha256-k9Ol++FFhQuPya6ZNQwVhqZZkMvE1ytLtbdrbH5zkrI=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "a566329bbcf0f915c4e6f42c2aee0354e1cd2c12",
|
"rev": "b15797faeca7494a7fe5fde2691d93affa3d3e37",
|
||||||
"revCount": 15401,
|
"revCount": 15239,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.clan.lol/clan/clan-core"
|
"url": "https://git.clan.lol/clan/clan-core"
|
||||||
},
|
},
|
||||||
@@ -210,11 +210,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1791886485,
|
"lastModified": 1788308203,
|
||||||
"narHash": "sha256-/+7ayxD7PeW6wMQiHw20UJHmSTGN8Y5HXbn0zG/EgoA=",
|
"narHash": "sha256-dCOb9YIJv9I2udjqukvjlGiTyOvGnO7zVbot0PxjBGk=",
|
||||||
"rev": "8d4d36437f9fff6ecaa34872bd53c3ed09ef5464",
|
"rev": "644c49bbf121b3e256bc83ce10b5d97813d9181d",
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/8d4d36437f9fff6ecaa34872bd53c3ed09ef5464.tar.gz"
|
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/644c49bbf121b3e256bc83ce10b5d97813d9181d.tar.gz"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
@@ -249,11 +249,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1789770686,
|
"lastModified": 1781152676,
|
||||||
"narHash": "sha256-uZkBR7yHdIKUFB5SZdfgh1qkGfI3XmYmI/lTiquxbck=",
|
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "725ea35e410ad83be4931d1bff7e090eacaf3563",
|
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -689,11 +689,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792366139,
|
"lastModified": 1786845137,
|
||||||
"narHash": "sha256-jto4pdsl0bgErQ5vaMc3gOyj//PILLRJuqX3I7fUk6s=",
|
"narHash": "sha256-oQFip+v0luP8NIxJzmiW4Wu8bILsbFWom5l0zonl8hQ=",
|
||||||
"owner": "nix-darwin",
|
"owner": "nix-darwin",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "f0ec2b3fae4df92ea7788359aef9e07bb88d720f",
|
"rev": "4cff07de74b50e64bdd68cd4e722ab5b6b35ee48",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -890,11 +890,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792506237,
|
"lastModified": 1788775869,
|
||||||
"narHash": "sha256-Q/8KLB6jNfpaUN1HAedE/u/Et9feE/zYSsutBLKrCFk=",
|
"narHash": "sha256-JRf1lSypbvKj6AzUZHpUA6YC1DirVuitZWOnRwcm+Ww=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5adc50831c259f87faeb09ddf930d43597197473",
|
"rev": "58973d74f1893afe13f5902919803a0e99da0ca4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1014,11 +1014,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792056300,
|
"lastModified": 1788337237,
|
||||||
"narHash": "sha256-YU1LjDe1o2Ymg5zEiCHbmMXLkDCtxHQRiVqZ3nC5GGk=",
|
"narHash": "sha256-gkSH8VUtCo6hnysNmb9DbTuDepH2t5pv+QWjP75xKAk=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "44931708c62937c2b1bf7953902989fa5bb9ee1b",
|
"rev": "fbf759290e0cb0a98dfc813a4eb7d53ad1dacb57",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1105,11 +1105,11 @@
|
|||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1791244623,
|
"lastModified": 1774449309,
|
||||||
"narHash": "sha256-uuanbkFk8a313Zfrxdcz8Us/yQtaxNnKJtX8v0ZPpd8=",
|
"narHash": "sha256-brhZ8DmuGtzkCYHJg4HEd602amKm89Y9ytsFZ5uWD1w=",
|
||||||
"owner": "nix-systems",
|
"owner": "nix-systems",
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"rev": "d36eeed142f88b5a636f2e3856a53c61654cf29b",
|
"rev": "c29398b59d2048c4ab79345812849c9bd15e9150",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -100,7 +100,6 @@
|
|||||||
inherit system;
|
inherit system;
|
||||||
overlays = [
|
overlays = [
|
||||||
inputs.self.overlays.packagesOverlay
|
inputs.self.overlays.packagesOverlay
|
||||||
inputs.self.overlays.phpPr576853Overlay # TEMPORARY, see overlays/nixpkgs-pr-576853-php.nix
|
|
||||||
];
|
];
|
||||||
config = { };
|
config = { };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -310,17 +310,10 @@
|
|||||||
"200:c8db:ea9b:5bdc:44ed:ad87:462a:6bd0"
|
"200:c8db:ea9b:5bdc:44ed:ad87:462a:6bd0"
|
||||||
];
|
];
|
||||||
roles.default.settings.extraPeers = [
|
roles.default.settings.extraPeers = [
|
||||||
"tls://133.18.201.69:54232"
|
"tls://ygg.jjolly.dev:3443"
|
||||||
"tls://153.120.42.137:54232"
|
"tls://[2602:fc24:18:7a42::1]:993"
|
||||||
"tcp://yg-sin.magicum.net:23901"
|
|
||||||
"tls://asia.deinfra.org:15015"
|
|
||||||
"tls://ygg5.mk16.de:1338?key=0000009611ae5391dc0aceea9f3fa6a0dc1279f4306059339e84bfb8b74d2f9b"
|
|
||||||
"tcp://leo.node.3dt.net:9002"
|
"tcp://leo.node.3dt.net:9002"
|
||||||
"tcp://ygg-kcmo.incognet.io:8883"
|
"tcp://ygg-kcmo.incognet.io:8883"
|
||||||
"tls://23.184.48.86:1443"
|
|
||||||
"tls://192.3.13.16:8118"
|
|
||||||
"tls://ygg-pa.incognet.io:8884"
|
|
||||||
"tls://mo.us.ygg.triplebit.org:993"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
|
self,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
self.nixosModules.sd-card-image
|
||||||
|
];
|
||||||
|
|
||||||
clan.core.settings.machine.description =
|
clan.core.settings.machine.description =
|
||||||
"VM machine for collecting prometheus metrics and fire alerts";
|
"VM machine for collecting prometheus metrics and fire alerts";
|
||||||
|
|
||||||
@@ -11,4 +16,10 @@
|
|||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
clan.core.sops.defaultGroups = [ "admins" ];
|
clan.core.sops.defaultGroups = [ "admins" ];
|
||||||
|
|
||||||
|
image.modules.sd-card = {
|
||||||
|
disabledModules = [
|
||||||
|
./disko.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
domain = "w-inventory.newedge.house";
|
domain = "poy-inventory.newedge.house";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
clan.core.settings.machine.description = "VM machine that host Inventree system for Poy";
|
clan.core.settings.machine.description = "VM machine that host Inventree system for Poy";
|
||||||
@@ -79,7 +79,6 @@ in
|
|||||||
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
secretKeyFile = config.clan.core.vars.generators.inventree.files.secret-key.path;
|
||||||
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
adminPasswordFile = config.clan.core.vars.generators.inventree.files.admin-password.path;
|
||||||
settings.INVENTREE_SITE_URL = "https://${domain}";
|
settings.INVENTREE_SITE_URL = "https://${domain}";
|
||||||
settings.INVENTREE_FRONTEND_SETTINGS = ''{"mobile_mode": "allow-ignore"}'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
|
|||||||
@@ -54,10 +54,7 @@ in
|
|||||||
self.nixosModules.think-gtcm
|
self.nixosModules.think-gtcm
|
||||||
self.nixosModules.think-backend-gtcm
|
self.nixosModules.think-backend-gtcm
|
||||||
];
|
];
|
||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [ self.overlays.packagesOverlay ];
|
||||||
self.overlays.packagesOverlay
|
|
||||||
self.overlays.phpPr576853Overlay # TEMPORARY, see overlays/nixpkgs-pr-576853-php.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
clan.core.vars.generators.greaterchiangmai = {
|
clan.core.vars.generators.greaterchiangmai = {
|
||||||
files = {
|
files = {
|
||||||
|
|||||||
@@ -10,6 +10,16 @@
|
|||||||
inputs.matthew-hardware.nixosModules.mt7986a-glinet-gl-mt6000
|
inputs.matthew-hardware.nixosModules.mt7986a-glinet-gl-mt6000
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# matthew-hardware's common/generic-uefi-image.nix still sets
|
||||||
|
# `image.repart.enable = true`; nixpkgs removed that option (importing
|
||||||
|
# image/repart.nix now always defines system.build.image). Declare it so the
|
||||||
|
# definition has somewhere to land. Drop once upstream stops setting it.
|
||||||
|
options.image.repart.enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
internal = true;
|
||||||
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
hardware.mt7986a-glinet-gl-mt6000.enable = true;
|
hardware.mt7986a-glinet-gl-mt6000.enable = true;
|
||||||
hardware.mt7986a-glinet-gl-mt6000.zealous = true;
|
hardware.mt7986a-glinet-gl-mt6000.zealous = true;
|
||||||
@@ -73,108 +83,6 @@
|
|||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
# The router service opens SSH only to the mgmt VLAN, the staging port
|
|
||||||
# and the ZeroTier mesh (inventories/router.nix). Also admit it over
|
|
||||||
# Yggdrasil (fleet-wide instance in inventories/default.nix) so the box
|
|
||||||
# stays reachable when ZeroTier's controller/relays are down. Scoped to
|
|
||||||
# the tunnel interface (clan-core's yggdrasil service names it `ygg`):
|
|
||||||
# only decrypted 200::/7 traffic arrives there, so a spoofed source on
|
|
||||||
# the WAN never matches.
|
|
||||||
networking.firewall.interfaces.${config.services.yggdrasil.settings.IfName}.allowedTCPPorts = [
|
|
||||||
22
|
|
||||||
];
|
|
||||||
|
|
||||||
# AIS delegates a single /64 over PPPoE (seen 2026-10-02: "ppp0: DHCP:
|
|
||||||
# received delegated prefix 2405:9800:b910:1f5d::/64"). The router
|
|
||||||
# service hands out sub-prefixes with SubnetId=auto, so whichever VLAN
|
|
||||||
# interface comes up first takes the whole /64 -- on this box vlan-mgmt
|
|
||||||
# did, and vlan-lan logged "Couldn't find a suitable prefix. Ran out of
|
|
||||||
# address space": no RA prefix on lan, so clients had no global IPv6.
|
|
||||||
# Pin the ids: lan (where the clients are) always gets subnet 0, i.e.
|
|
||||||
# the entire prefix when it is a /64; mgmt asks for subnet 1, which
|
|
||||||
# fails harmlessly on a /64 (mgmt is reached over v4 and the meshes) and
|
|
||||||
# starts working unchanged if AIS ever delegates wider. Drop once
|
|
||||||
# cnx-network's router service exposes a per-VLAN subnet id.
|
|
||||||
systemd.network.networks."40-vlan-lan".dhcpPrefixDelegationConfig.SubnetId = lib.mkForce "0x0";
|
|
||||||
systemd.network.networks."40-vlan-mgmt".dhcpPrefixDelegationConfig.SubnetId = lib.mkForce "0x1";
|
|
||||||
|
|
||||||
# Interim copy of cnx-network-clan 45643d6 (router/ipv6: deprecate
|
|
||||||
# withdrawn DHCPv6-PD prefixes); drop when the input is bumped past it.
|
|
||||||
# networkd never deprecates a prefix it stops announcing (systemd#29651):
|
|
||||||
# on every reboot the first PPPoE session lands on another AIS BNG, is
|
|
||||||
# torn down minutes later, and the reconnect brings a different /64.
|
|
||||||
# networkd announces the new prefix at once but clients keep the old
|
|
||||||
# one preferred for the rest of its 1800 s lifetime, so RFC 6724 source
|
|
||||||
# selection picks the dead prefix and every v6 connection fails for up
|
|
||||||
# to 30 minutes (2026-10-08 07:57). Watch vlan-* for networkd removing a
|
|
||||||
# dynamic (PD) address and send up to three RAs with that /64 at
|
|
||||||
# preferred lifetime 0, one short-lived radvd per RA (SIGKILLed: no
|
|
||||||
# shutdown RA with router lifetime 0), stopping as soon as the prefix is
|
|
||||||
# back on the interface -- a plain flap re-delegates the same /64 within
|
|
||||||
# seconds and a deprecating RA sent after networkd re-announced it would
|
|
||||||
# leave clients deprecated until the next periodic RA (08:19 today).
|
|
||||||
systemd.services.router-radv-deprecate = {
|
|
||||||
description = "Deprecate withdrawn DHCPv6-PD prefixes on the VLANs";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "systemd-networkd.service" ];
|
|
||||||
path = [
|
|
||||||
pkgs.iproute2
|
|
||||||
pkgs.ipcalc
|
|
||||||
pkgs.radvd
|
|
||||||
];
|
|
||||||
serviceConfig = {
|
|
||||||
Restart = "always";
|
|
||||||
RestartSec = 2;
|
|
||||||
RuntimeDirectory = "router-radv-deprecate";
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
conf=$RUNTIME_DIRECTORY/radvd.conf
|
|
||||||
pid=$RUNTIME_DIRECTORY/radvd.pid
|
|
||||||
|
|
||||||
# /64s of the global addresses currently on an interface.
|
|
||||||
prefixes_on() {
|
|
||||||
ip -o -6 addr show dev "$1" scope global | while read -r _ _ _ a _; do
|
|
||||||
echo "$(ipcalc --no-decorate -n "$a")/''${a##*/}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# "Deleted 4: vlan-lan inet6 2001:db8::1/64 scope global dynamic mngtmpaddr \ valid_lft ..."
|
|
||||||
ip -o -6 monitor address | while read -r event _ dev _ addr _ scope rest; do
|
|
||||||
[[ $event == Deleted && $dev == vlan-* && $scope == global && $rest == *dynamic* ]] || continue
|
|
||||||
prefix="$(ipcalc --no-decorate -n "$addr")/''${addr##*/}"
|
|
||||||
cat > "$conf" <<EOF
|
|
||||||
interface $dev {
|
|
||||||
AdvSendAdvert on;
|
|
||||||
MinRtrAdvInterval 3;
|
|
||||||
MaxRtrAdvInterval 4;
|
|
||||||
AdvDefaultLifetime 1800;
|
|
||||||
AdvCurHopLimit 0;
|
|
||||||
prefix $prefix {
|
|
||||||
AdvOnLink on;
|
|
||||||
AdvAutonomous on;
|
|
||||||
AdvPreferredLifetime 0;
|
|
||||||
AdvValidLifetime 7200;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
EOF
|
|
||||||
for _ in 1 2 3; do
|
|
||||||
if prefixes_on "$dev" | grep -qxF "$prefix"; then
|
|
||||||
echo "$dev: $prefix is back, leaving it announced"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "$dev: deprecating withdrawn prefix $prefix"
|
|
||||||
rm -f "$pid"
|
|
||||||
# radvd sends its first RA on start; the next would follow 3-4 s later.
|
|
||||||
radvd -C "$conf" -p "$pid" -n -m stderr &
|
|
||||||
sleep 1
|
|
||||||
kill -KILL $! 2>/dev/null || true
|
|
||||||
wait $! || true
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.heartbeat-push = {
|
systemd.services.heartbeat-push = {
|
||||||
description = "Heartbeat push to uptime monitor";
|
description = "Heartbeat push to uptime monitor";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
{ self, ... }:
|
|
||||||
{
|
{
|
||||||
system.stateVersion = "25.11";
|
system.stateVersion = "25.11";
|
||||||
nixpkgs.hostPlatform = {
|
nixpkgs.hostPlatform = {
|
||||||
@@ -30,7 +29,4 @@
|
|||||||
security.acme.acceptTerms = true;
|
security.acme.acceptTerms = true;
|
||||||
|
|
||||||
imports = [ ];
|
imports = [ ];
|
||||||
|
|
||||||
# TEMPORARY: wordpress php-fpm pools use pkgs.php; see overlays/nixpkgs-pr-576853-php.nix
|
|
||||||
nixpkgs.overlays = [ self.overlays.phpPr576853Overlay ];
|
|
||||||
}
|
}
|
||||||
@@ -82,11 +82,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
services.avahi = {
|
services.avahi = {
|
||||||
settings.publish = {
|
publish.enable = true;
|
||||||
disable-publishing = false;
|
publish.userServices = true;
|
||||||
disable-user-service-publishing = false;
|
|
||||||
publish-addresses = true;
|
|
||||||
};
|
|
||||||
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
|
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
|
||||||
nssmdns4 = true;
|
nssmdns4 = true;
|
||||||
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
|
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
|
||||||
|
|||||||
@@ -80,12 +80,15 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
dictionaries = pkgs.hunspell.withDicts (
|
dictionaries =
|
||||||
d: with d; [
|
with pkgs;
|
||||||
|
(hunspellWithDicts (
|
||||||
|
with hunspellDicts;
|
||||||
|
[
|
||||||
en-us-large
|
en-us-large
|
||||||
th-th
|
th-th
|
||||||
]
|
]
|
||||||
);
|
));
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [ inputs.plasma-manager.homeModules.plasma-manager ];
|
imports = [ inputs.plasma-manager.homeModules.plasma-manager ];
|
||||||
@@ -108,6 +111,7 @@
|
|||||||
keepassxc
|
keepassxc
|
||||||
vlc
|
vlc
|
||||||
thunderbird
|
thunderbird
|
||||||
|
wasabiwallet
|
||||||
protonmail-desktop
|
protonmail-desktop
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -115,8 +119,6 @@
|
|||||||
|
|
||||||
programs.firefox = {
|
programs.firefox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
# Keep the pre-26.05 profile location; moving it would orphan the existing profile.
|
|
||||||
configPath = ".mozilla/firefox";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
programs.home-manager.enable = true;
|
programs.home-manager.enable = true;
|
||||||
|
|||||||
@@ -42,27 +42,10 @@
|
|||||||
dbhost = lib.mkDefault "/run/postgresql";
|
dbhost = lib.mkDefault "/run/postgresql";
|
||||||
dbuser = lib.mkDefault "nextcloud";
|
dbuser = lib.mkDefault "nextcloud";
|
||||||
dbname = lib.mkDefault "nextcloud";
|
dbname = lib.mkDefault "nextcloud";
|
||||||
};
|
adminuser = lib.mkDefault "admin";
|
||||||
|
adminpassFile = lib.mkDefault config.clan.core.vars.generators.nextcloud.files.adminpassFile.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
# nixpkgs installs Nextcloud with --disable-admin-user; create the
|
|
||||||
# admin account ourselves once the instance is installed.
|
|
||||||
systemd.services.nextcloud-admin-user = {
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "nextcloud-setup.service" ];
|
|
||||||
requires = [ "nextcloud-setup.service" ];
|
|
||||||
before = [ "phpfpm-nextcloud.service" ];
|
|
||||||
path = [ config.services.nextcloud.occ ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "nextcloud";
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
if ! nextcloud-occ user:info admin >/dev/null 2>&1; then
|
|
||||||
NC_PASS="$(<${config.clan.core.vars.generators.nextcloud.files.adminpassFile.path})" \
|
|
||||||
nextcloud-occ user:add admin --password-from-env --group admin
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
clan.core.state.nextcloud.folders = [
|
clan.core.state.nextcloud.folders = [
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
server.wait_for_unit("phpfpm-nextcloud.service")
|
server.wait_for_unit("phpfpm-nextcloud.service")
|
||||||
|
|
||||||
# Admin account exists and is in the admin group
|
# Check that garage is running
|
||||||
server.succeed("nextcloud-occ user:info admin --output=json | grep -q '\"groups\":\\[\"admin\"\\]'")
|
server.succeed("systemctl status phpfpm-nextcloud.service")
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@
|
|||||||
throw "clanService/yggdrasil is required";
|
throw "clanService/yggdrasil is required";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
clan.core.vars.generators.phonebox = {
|
clan.core.vars.generators.phonebox = builtins.break {
|
||||||
files = {
|
files = {
|
||||||
server-prefix-number.secret = false;
|
server-prefix-number.secret = false;
|
||||||
ata-local-number.secret = false;
|
ata-local-number.secret = false;
|
||||||
@@ -330,7 +330,6 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
"${matchAll}".allowedTCPPorts = [ 80 ];
|
"${matchAll}".allowedTCPPorts = [ 80 ];
|
||||||
"ygg".allowedTCPPorts = [ 80 ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.asterisk = {
|
services.asterisk = {
|
||||||
@@ -404,7 +403,7 @@
|
|||||||
in
|
in
|
||||||
''
|
''
|
||||||
[from-internal]
|
[from-internal]
|
||||||
exten => 99,1,Answer()
|
exten => 999,1,Answer()
|
||||||
same => n,Playback(hello-world)
|
same => n,Playback(hello-world)
|
||||||
same => n,Hangup()
|
same => n,Hangup()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# Prometheus
|
|
||||||
|
|
||||||
Monitoring over the Yggdrasil network. Requires the `yggdrasil` clan service on every machine in the instance.
|
|
||||||
|
|
||||||
Roles:
|
|
||||||
|
|
||||||
- `server`: runs Prometheus and Alertmanager, scraping every `nodes` machine by its Yggdrasil address. Optional `matrix-alertmanager` forwards alerts to Matrix rooms; the bot token and webhook secret are generated as clan vars.
|
|
||||||
- `nodes`: enables the `services.prometheus.exporters` listed in `exporters` (with `openFirewall`) so the server can harvest them.
|
|
||||||
@@ -239,11 +239,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
services.avahi = {
|
services.avahi = {
|
||||||
settings.publish = {
|
publish.enable = true;
|
||||||
disable-publishing = false;
|
publish.userServices = true;
|
||||||
disable-user-service-publishing = false;
|
|
||||||
publish-addresses = true;
|
|
||||||
};
|
|
||||||
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
|
# ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
|
||||||
nssmdns4 = true;
|
nssmdns4 = true;
|
||||||
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
|
# ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
|
||||||
|
|||||||
@@ -9,5 +9,8 @@
|
|||||||
think-backend-gtcm = {
|
think-backend-gtcm = {
|
||||||
imports = [ ./think-backend-gtcm.nix ];
|
imports = [ ./think-backend-gtcm.nix ];
|
||||||
};
|
};
|
||||||
|
sd-card-image = {
|
||||||
|
imports = [ ./sd-card-image.nix ];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
# Local copy of the clan-core module in patches/upstream-sd-card-secret-injection.patch.
|
||||||
|
# Delete once clan-core ships clan.core.image.sd-card.addFilesScript
|
||||||
|
# (the option declaration would conflict).
|
||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
parseAddFilesArgs = "${inputs.clan-core}/nixosModules/clanCore/image/parse-add-files-args.sh";
|
||||||
|
|
||||||
|
# Writes extra files straight into the ext4 root partition of an SD image
|
||||||
|
# with debugfs, so no boot-time hook is needed: the files exist on the root
|
||||||
|
# filesystem from the first boot. Runs unprivileged and without mounting.
|
||||||
|
sdCardAddFilesScript = pkgs.writeShellApplication {
|
||||||
|
name = "add-files";
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
coreutils
|
||||||
|
diffutils
|
||||||
|
e2fsprogs
|
||||||
|
findutils
|
||||||
|
util-linux
|
||||||
|
zstd
|
||||||
|
];
|
||||||
|
extraShellCheckFlags = [ "-x" ];
|
||||||
|
text = ''
|
||||||
|
# shellcheck source=${parseAddFilesArgs}
|
||||||
|
source ${parseAddFilesArgs}
|
||||||
|
|
||||||
|
# If no extra paths, just copy source to output
|
||||||
|
if [[ ''${#extra_paths[@]} -eq 0 ]]; then
|
||||||
|
cp -- "$source_image" "$output_image"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The injection sandbox mounts /tmp as a RAM-backed tmpfs and a raw SD
|
||||||
|
# image is several GiB, so stage it next to the output, the only
|
||||||
|
# writable on-disk path.
|
||||||
|
workdir=$(dirname "$output_image")
|
||||||
|
img="$workdir/.$(basename "$output_image").raw"
|
||||||
|
cmds=$(mktemp)
|
||||||
|
trap 'rm -f -- "$img" "$cmds"' EXIT
|
||||||
|
|
||||||
|
case "$source_image" in
|
||||||
|
*.zst)
|
||||||
|
zstd -d -q --no-progress -o "$img" -- "$source_image"
|
||||||
|
compress=1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
cp --no-preserve=mode -- "$source_image" "$img"
|
||||||
|
compress=0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
chmod u+w -- "$img"
|
||||||
|
|
||||||
|
# sd-image.nix writes an MBR whose ext4 root is the (only) type-83 partition.
|
||||||
|
root_start=""
|
||||||
|
while read -r _ start type; do
|
||||||
|
if [[ "$type" == "0x83" ]]; then
|
||||||
|
root_start=$start
|
||||||
|
fi
|
||||||
|
done < <(partx --show -g -o NR,START,TYPE -- "$img")
|
||||||
|
if [[ -z "$root_start" ]]; then
|
||||||
|
echo "No Linux (type 83) root partition found in $source_image" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# e2fsprogs opens a partition inside a disk image via "<file>?offset=<bytes>".
|
||||||
|
rootfs="$img?offset=$((root_start * 512))"
|
||||||
|
|
||||||
|
# Build a debugfs command script. debugfs has neither mkdir -p nor a
|
||||||
|
# recursive write, so directories are created ancestor-first ("already
|
||||||
|
# exists" errors are harmless). write copies the source mode; ownership
|
||||||
|
# is forced to root since the script runs as an unprivileged user.
|
||||||
|
declare -A made_dirs=()
|
||||||
|
written=()
|
||||||
|
emit_mkdir_p() {
|
||||||
|
local dir="$1" path="" part
|
||||||
|
IFS=/ read -ra parts <<< "$dir"
|
||||||
|
for part in "''${parts[@]}"; do
|
||||||
|
[[ -z "$part" ]] && continue
|
||||||
|
path="$path/$part"
|
||||||
|
if [[ -z "''${made_dirs[$path]:-}" ]]; then
|
||||||
|
echo "mkdir \"$path\""
|
||||||
|
made_dirs[$path]=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
emit_write() {
|
||||||
|
local src="$1" dest="$2"
|
||||||
|
echo "write \"$src\" \"$dest\""
|
||||||
|
echo "sif \"$dest\" uid 0"
|
||||||
|
echo "sif \"$dest\" gid 0"
|
||||||
|
written+=("$src" "$dest")
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((i = 0; i < ''${#extra_paths[@]}; i += 2)); do
|
||||||
|
local_src="''${extra_paths[i]}"
|
||||||
|
# Normalize to an absolute path without trailing slash; "/" becomes "".
|
||||||
|
target="/''${extra_paths[i+1]#/}"
|
||||||
|
target="''${target%/}"
|
||||||
|
|
||||||
|
if [[ -d "$local_src" ]]; then
|
||||||
|
emit_mkdir_p "$target"
|
||||||
|
while IFS= read -r -d "" entry; do
|
||||||
|
dest="$target/''${entry#"$local_src"/}"
|
||||||
|
if [[ -d "$entry" ]]; then
|
||||||
|
emit_mkdir_p "$dest"
|
||||||
|
elif [[ -f "$entry" ]]; then
|
||||||
|
emit_write "$entry" "$dest"
|
||||||
|
else
|
||||||
|
echo "Unsupported file type (only regular files and directories): $entry" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done < <(
|
||||||
|
# Never treat our own staging/output directory as input, even if
|
||||||
|
# the caller placed it inside an extra path.
|
||||||
|
find "$local_src" -mindepth 1 -path "$workdir" -prune -o -print0
|
||||||
|
)
|
||||||
|
elif [[ -f "$local_src" ]]; then
|
||||||
|
emit_mkdir_p "$(dirname "$target")"
|
||||||
|
emit_write "$local_src" "$target"
|
||||||
|
else
|
||||||
|
echo "Extra path does not exist: $local_src" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done > "$cmds"
|
||||||
|
|
||||||
|
debugfs -w -f "$cmds" "$rootfs"
|
||||||
|
|
||||||
|
# debugfs exits 0 even when commands fail: verify every file landed
|
||||||
|
# intact and the filesystem is still consistent.
|
||||||
|
for ((i = 0; i < ''${#written[@]}; i += 2)); do
|
||||||
|
if ! debugfs -R "cat \"''${written[i+1]}\"" "$rootfs" 2>/dev/null | cmp -s -- - "''${written[i]}"; then
|
||||||
|
echo "Failed to write ''${written[i+1]} into the root filesystem" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ! fsck_output=$(EXT2FS_NO_MTAB_OK=yes e2fsck -fn "$rootfs" 2>&1); then
|
||||||
|
echo "Root filesystem is inconsistent after injection:" >&2
|
||||||
|
echo "$fsck_output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$compress" == 1 ]]; then
|
||||||
|
zstd -T0 -q --no-progress --rm -o "$output_image" -- "$img"
|
||||||
|
else
|
||||||
|
mv -- "$img" "$output_image"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.clan.core.image = {
|
||||||
|
sd-card.addFilesScript = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = sdCardAddFilesScript;
|
||||||
|
defaultText = lib.literalExpression "pkgs.writeShellApplication { ... }";
|
||||||
|
description = ''
|
||||||
|
Script to inject extra files into an SD card image
|
||||||
|
(`clan machines build <machine> --format sd-card`).
|
||||||
|
|
||||||
|
Files are written directly into the ext4 root partition, so they are
|
||||||
|
present from the first boot without any initrd hook. Both compressed
|
||||||
|
(`.img.zst`, the `sdImage.compressImage` default) and raw `.img`
|
||||||
|
images are supported; the output keeps the source's format.
|
||||||
|
|
||||||
|
Source `parse-add-files-args.sh` to get standardized argument parsing.
|
||||||
|
After sourcing, `$source_image`, `$output_image`, and `$extra_paths`
|
||||||
|
are available.
|
||||||
|
|
||||||
|
Accepts the following arguments:
|
||||||
|
- --source `<path>`: Path to the original built image (read-only)
|
||||||
|
- --output `<path>`: Path for the modified output image
|
||||||
|
- --extra-path `<local-source>` `<target-path>`: Repeatable, maps a local
|
||||||
|
file or directory to a target filesystem path inside the image
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@
|
|||||||
{
|
{
|
||||||
flake.overlays = {
|
flake.overlays = {
|
||||||
packagesOverlay = import ../pkgs/overlay.nix;
|
packagesOverlay = import ../pkgs/overlay.nix;
|
||||||
# TEMPORARY: drop with the file once NixOS/nixpkgs#576853 reaches nixpkgs-unstable.
|
|
||||||
phpPr576853Overlay = import ./nixpkgs-pr-576853-php.nix;
|
|
||||||
|
|
||||||
frappixFrappeOverlay = inputs.frappix.frappeOverlay.x86_64-linux;
|
frappixFrappeOverlay = inputs.frappix.frappeOverlay.x86_64-linux;
|
||||||
frappixLibsOverlay = inputs.frappix.libsOverlay.x86_64-linux;
|
frappixLibsOverlay = inputs.frappix.libsOverlay.x86_64-linux;
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
# TEMPORARY backport of NixOS/nixpkgs#576853
|
|
||||||
# (php 8.3.36 / 8.4.27 / 8.5.12; fixes CVE-2026-109618, CVE-2026-109619).
|
|
||||||
# Merged to master 2026-10-21, not yet on nixpkgs-unstable.
|
|
||||||
# Track: https://track.nix.ee/?pr=576853
|
|
||||||
#
|
|
||||||
# Remove once `nix flake update` pulls it in: delete this file and every
|
|
||||||
# reference to it (grep 576853). The overlay becomes a no-op and warns at eval
|
|
||||||
# time as soon as the locked nixpkgs ships these versions or newer.
|
|
||||||
final: prev:
|
|
||||||
let
|
|
||||||
inherit (prev) lib;
|
|
||||||
|
|
||||||
updates = {
|
|
||||||
php83 = {
|
|
||||||
version = "8.3.36";
|
|
||||||
hash = "sha256-RvS0XjaI7SsYGQFae1MsFbUcwFW907E/0A3f7r7tzV8=";
|
|
||||||
};
|
|
||||||
php84 = {
|
|
||||||
version = "8.4.27";
|
|
||||||
hash = "sha256-slMKh3xyL0sIyeF0owywi6wrfhkD7m6eMV8KqfX4LCQ=";
|
|
||||||
};
|
|
||||||
php85 = {
|
|
||||||
version = "8.5.12";
|
|
||||||
hash = "sha256-/7YO3EN87WO1E35JhNs3moERxQBJxxFRDYuVgnfvwag=";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# php-packages.nix hunk of the PR: the new releases' filter OOM tests only
|
|
||||||
# pass when the freshly built module is loaded explicitly.
|
|
||||||
packageOverrides = _: phpPrev: {
|
|
||||||
extensions = phpPrev.extensions // {
|
|
||||||
filter = phpPrev.extensions.filter.override {
|
|
||||||
postConfigure = ''
|
|
||||||
substituteInPlace \
|
|
||||||
tests/oom_filter_input.phpt \
|
|
||||||
tests/oom_filter_input_array.phpt \
|
|
||||||
tests/oom_filter_default_encoded.phpt \
|
|
||||||
--replace-fail "'-n'," "'-n', '-d', 'extension=$PWD/modules/filter.so',"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
bump =
|
|
||||||
name:
|
|
||||||
{ version, hash }:
|
|
||||||
if lib.versionAtLeast prev.${name}.version version then
|
|
||||||
lib.warn
|
|
||||||
"nixpkgs ships ${name} ${prev.${name}.version} (>= ${version}): NixOS/nixpkgs#576853 has landed, remove overlays/nixpkgs-pr-576853-php.nix"
|
|
||||||
prev.${name}
|
|
||||||
else
|
|
||||||
# `.override` on a php buildEnv rebuilds the interpreter with the new
|
|
||||||
# args, but the extension scope (php-packages) keeps the closure's
|
|
||||||
# original `packageOverrides`. Only `overrideAttrs` re-enters
|
|
||||||
# generic.nix with the overridden args and rebuilds that scope, so a
|
|
||||||
# no-op `overrideAttrs` is what makes `packageOverrides` take effect.
|
|
||||||
(prev.${name}.override { inherit version hash packageOverrides; }).overrideAttrs (_: { });
|
|
||||||
in
|
|
||||||
lib.mapAttrs bump updates
|
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
diff --git a/clan_lib/machines/build.py b/clan_lib/machines/build.py
|
||||||
|
index 00e2b16..d9ed5a1 100644
|
||||||
|
--- a/clan_lib/machines/build.py
|
||||||
|
+++ b/clan_lib/machines/build.py
|
||||||
|
@@ -98,32 +98,42 @@ def _hostplatform_unset_hint(machine: Machine, build_format: str) -> str:
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def _is_image_format(build_format: str) -> bool:
|
||||||
|
- """Check if the build format is an image format that supports secret injection."""
|
||||||
|
- return build_format not in ("toplevel")
|
||||||
|
+def _image_add_files_script(machine: Machine, build_format: str) -> str | None:
|
||||||
|
+ """Return the format's addFilesScript store path.
|
||||||
|
+
|
||||||
|
+ None when clan has no file-injection support for this format
|
||||||
|
+ (i.e. no `clan.core.image.<format>.addFilesScript` option), in which
|
||||||
|
+ case secret injection must be skipped rather than fail.
|
||||||
|
+ """
|
||||||
|
+ result = machine.flake.select_machine(
|
||||||
|
+ machine.name,
|
||||||
|
+ f"config.clan.core.image.?{build_format}.addFilesScript",
|
||||||
|
+ )
|
||||||
|
+ return result.get(build_format)
|
||||||
|
|
||||||
|
|
||||||
|
-def _find_image_file(build_dir: Path, build_format: str) -> Path:
|
||||||
|
- """Find the actual image file within a build output directory.
|
||||||
|
+def _find_image_file(machine: Machine, build_dir: Path, build_format: str) -> Path:
|
||||||
|
+ """Resolve the image file within an image build output directory.
|
||||||
|
|
||||||
|
- Image builds output a directory (e.g., /nix/store/...-x86_64-linux.iso/)
|
||||||
|
- containing the image file in a subdirectory named after the format
|
||||||
|
- (e.g., iso/nixos-26.05-x86_64-linux.iso).
|
||||||
|
+ Image builds output a directory whose layout is format-specific
|
||||||
|
+ (iso/nixos-….iso, sd-image/nixos-….img.zst, …). nixpkgs exposes the
|
||||||
|
+ relative location as `config.image.filePath`, attached to the image
|
||||||
|
+ derivation as `passthru.filePath`.
|
||||||
|
"""
|
||||||
|
- format_dir = build_dir / build_format
|
||||||
|
- if not format_dir.is_dir():
|
||||||
|
- msg = f"Expected directory {format_dir} not found in build output"
|
||||||
|
- raise ClanError(msg)
|
||||||
|
- files = [f for f in format_dir.iterdir() if f.is_file()]
|
||||||
|
- if len(files) != 1:
|
||||||
|
- msg = f"Expected exactly one file in {format_dir}, found {len(files)}"
|
||||||
|
+ file_path: str = machine.flake.select_machine(
|
||||||
|
+ machine.name,
|
||||||
|
+ f"config.system.build.images.{build_format}.passthru.filePath",
|
||||||
|
+ )
|
||||||
|
+ image_file = build_dir / file_path
|
||||||
|
+ if not image_file.is_file():
|
||||||
|
+ msg = f"Expected image file {image_file} not found in build output"
|
||||||
|
raise ClanError(msg)
|
||||||
|
- return files[0]
|
||||||
|
+ return image_file
|
||||||
|
|
||||||
|
|
||||||
|
def _inject_secrets_into_image(
|
||||||
|
machine: Machine,
|
||||||
|
- build_format: str,
|
||||||
|
+ add_files_script: str,
|
||||||
|
source_image_path: Path,
|
||||||
|
output_path: Path,
|
||||||
|
*,
|
||||||
|
@@ -137,7 +147,9 @@ def _inject_secrets_into_image(
|
||||||
|
secrets_target_dir = machine.secret_vars_store.get_upload_directory(machine.name)
|
||||||
|
|
||||||
|
with TemporaryDirectory(prefix="clan-build-secrets-") as tmpdir:
|
||||||
|
- base_dir = Path(tmpdir).resolve()
|
||||||
|
+ # The tree mapped to "/" inside the image lives in its own subdir so
|
||||||
|
+ # the format script never sees its own output as input.
|
||||||
|
+ base_dir = Path(tmpdir).resolve() / "root"
|
||||||
|
upload_dir = base_dir / secrets_target_dir.lstrip("/")
|
||||||
|
upload_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
@@ -153,12 +165,6 @@ def _inject_secrets_into_image(
|
||||||
|
if not any(upload_dir.iterdir()):
|
||||||
|
return False
|
||||||
|
|
||||||
|
- # Get the addFilesScript path from the NixOS config
|
||||||
|
- script_path = machine.flake.select_machine(
|
||||||
|
- machine.name,
|
||||||
|
- f"config.clan.core.image.{build_format}.addFilesScript",
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
# Remove stale output from previous runs (xorriso refuses to
|
||||||
|
# write to an existing non-empty file when indev != outdev)
|
||||||
|
output = Path(output_path)
|
||||||
|
@@ -167,12 +173,12 @@ def _inject_secrets_into_image(
|
||||||
|
|
||||||
|
# Write the image into the tmpdir so the sandbox only needs
|
||||||
|
# write access to the temp directory, not the final destination.
|
||||||
|
- sandbox_output = base_dir / "output" / output.name
|
||||||
|
+ sandbox_output = base_dir.parent / "output" / output.name
|
||||||
|
sandbox_output.parent.mkdir(parents=True)
|
||||||
|
|
||||||
|
# Execute the addFilesScript; clean up partial output on failure
|
||||||
|
inject_cmd: list[str] = [
|
||||||
|
- f"{script_path}/bin/add-files",
|
||||||
|
+ f"{add_files_script}/bin/add-files",
|
||||||
|
"--source",
|
||||||
|
str(source_image_path),
|
||||||
|
"--output",
|
||||||
|
@@ -265,33 +271,40 @@ def build_machine(
|
||||||
|
if build_path and (store := nix_test_store()):
|
||||||
|
build_path = store / str(build_path).lstrip("/")
|
||||||
|
|
||||||
|
- # Post-build secret injection for image formats
|
||||||
|
+ # Post-build secret injection for image formats that support it
|
||||||
|
if (
|
||||||
|
build_path
|
||||||
|
- and _is_image_format(options.format)
|
||||||
|
+ and options.format != "toplevel"
|
||||||
|
and not options.no_secrets
|
||||||
|
and not options.dry_run
|
||||||
|
):
|
||||||
|
- # build_path is a directory; resolve the actual image file inside
|
||||||
|
- source_file = _find_image_file(build_path, options.format)
|
||||||
|
- # Output to a file next to the symlink, named after the source file
|
||||||
|
- output_file = Path.cwd() / source_file.name
|
||||||
|
-
|
||||||
|
- secrets_injected = _inject_secrets_into_image(
|
||||||
|
- machine=machine,
|
||||||
|
- build_format=options.format,
|
||||||
|
- source_image_path=source_file,
|
||||||
|
- output_path=output_file,
|
||||||
|
- use_sandbox=options.use_sandbox,
|
||||||
|
- )
|
||||||
|
- if secrets_injected:
|
||||||
|
- log.warning(
|
||||||
|
- "This image contains secret key material "
|
||||||
|
- "(age private key). Treat this image as a sensitive "
|
||||||
|
- "artifact. Do not store it on shared or public media "
|
||||||
|
- "without encryption. Securely erase after use."
|
||||||
|
+ add_files_script = _image_add_files_script(machine, options.format)
|
||||||
|
+ if add_files_script is None:
|
||||||
|
+ log.info(
|
||||||
|
+ f"Image format '{options.format}' has no addFilesScript; "
|
||||||
|
+ "skipping secret injection"
|
||||||
|
)
|
||||||
|
- build_path = output_file
|
||||||
|
+ else:
|
||||||
|
+ # build_path is a directory; resolve the actual image file inside
|
||||||
|
+ source_file = _find_image_file(machine, build_path, options.format)
|
||||||
|
+ # Output to a file next to the symlink, named after the source file
|
||||||
|
+ output_file = Path.cwd() / source_file.name
|
||||||
|
+
|
||||||
|
+ secrets_injected = _inject_secrets_into_image(
|
||||||
|
+ machine=machine,
|
||||||
|
+ add_files_script=add_files_script,
|
||||||
|
+ source_image_path=source_file,
|
||||||
|
+ output_path=output_file,
|
||||||
|
+ use_sandbox=options.use_sandbox,
|
||||||
|
+ )
|
||||||
|
+ if secrets_injected:
|
||||||
|
+ log.warning(
|
||||||
|
+ "This image contains secret key material "
|
||||||
|
+ "(age private key). Treat this image as a sensitive "
|
||||||
|
+ "artifact. Do not store it on shared or public media "
|
||||||
|
+ "without encryption. Securely erase after use."
|
||||||
|
+ )
|
||||||
|
+ build_path = output_file
|
||||||
|
|
||||||
|
return BuildResult(
|
||||||
|
machine_name=machine.name,
|
||||||
@@ -0,0 +1,814 @@
|
|||||||
|
From 78a3a5154f42f39bdeb913526439d51cd879f67c Mon Sep 17 00:00:00 2001
|
||||||
|
From: kurogeek <kurogeek@users.noreply.github.com>
|
||||||
|
Date: Fri, 25 Sep 2026 10:37:05 +0000
|
||||||
|
Subject: [PATCH] clan machines build --format sd-card: fix image lookup, add
|
||||||
|
secret injection
|
||||||
|
|
||||||
|
`clan machines build <m> --format sd-card` failed after a successful nix
|
||||||
|
build:
|
||||||
|
|
||||||
|
Expected directory /nix/store/...-nixos-image-sd-card-...img.zst/sd-card not found in build output
|
||||||
|
|
||||||
|
Three bugs in the post-build secret injection step (clan_lib/machines/
|
||||||
|
build.py):
|
||||||
|
|
||||||
|
1. _find_image_file assumed the image lives at $out/<format>/. That only
|
||||||
|
holds for iso. nixpkgs' sd-card image writes $out/sd-image/<name>.img.zst
|
||||||
|
(nixos/modules/installer/sd-card/sd-image.nix). nixpkgs exposes the
|
||||||
|
real location as config.image.filePath, attached to every
|
||||||
|
system.build.images.<format> derivation as passthru.filePath. Use it.
|
||||||
|
|
||||||
|
2. Injection ran for every non-toplevel format and then selected
|
||||||
|
config.clan.core.image.<format>.addFilesScript, which only iso
|
||||||
|
defines. Probe with the maybe-selector and skip injection (with a log
|
||||||
|
line) when the format has no addFilesScript, instead of failing after
|
||||||
|
an expensive image build. --no-secrets is no longer needed for such
|
||||||
|
formats.
|
||||||
|
|
||||||
|
3. The tree passed as `--extra-path <dir> /` and the sandbox output
|
||||||
|
directory were the same directory, so every addFilesScript saw its
|
||||||
|
own output as input. The iso script tars the tree before xorriso
|
||||||
|
writes, so it shipped an empty /output directory in every ISO; a
|
||||||
|
script that stages a working copy of the image next to the output
|
||||||
|
would try to inject that copy into itself. Keep them as siblings:
|
||||||
|
tmpdir/root is mapped to /, tmpdir/output is the only writable path.
|
||||||
|
|
||||||
|
Even with those fixed, sd-card images shipped without the age key, so a
|
||||||
|
freshly flashed machine could not decrypt its secrets on first boot.
|
||||||
|
|
||||||
|
Add clan.core.image.sd-card.addFilesScript. Unlike the ISO (read-only
|
||||||
|
squashfs, needs an initrd hook to unpack /extra-files.tar), the SD
|
||||||
|
image's root is a plain ext4 partition, so the files are written
|
||||||
|
directly into it with debugfs: no boot-time component, no mount, no
|
||||||
|
privileges. The script locates the type-83 root partition with partx,
|
||||||
|
opens it via e2fsprogs' "<img>?offset=<bytes>" syntax, generates a
|
||||||
|
debugfs command script (mkdir ancestors, write, sif uid/gid 0), then
|
||||||
|
verifies every written file with cmp against the source and runs
|
||||||
|
e2fsck -fn, since debugfs exits 0 even when a command fails. Both
|
||||||
|
.img.zst (sdImage.compressImage default) and raw .img inputs are
|
||||||
|
handled; the output keeps the input's format. The raw image is staged
|
||||||
|
next to the output because the sandbox's /tmp is a RAM tmpfs.
|
||||||
|
|
||||||
|
checks/clan-test-sd-card mirrors clan-test-iso: generate vars, run
|
||||||
|
`clan machines build peer1 --format sd-card` through the real CLI and
|
||||||
|
injection sandbox, boot the injected image in QEMU and verify over SSH
|
||||||
|
that a generated secret is decrypted (sops-nix found the injected age
|
||||||
|
key) and that /var/lib/sops-nix/key.txt is root:root 0600. The x86_64
|
||||||
|
sd image boots via extlinux.conf, which QEMU cannot load by itself
|
||||||
|
(nixpkgs' boot-uboot-extlinux test is marked broken), so the test does
|
||||||
|
what a bootloader would: extract kernel, initrd and APPEND from /boot on
|
||||||
|
the image's root partition with debugfs and pass them to QEMU with
|
||||||
|
-kernel/-initrd/-append.
|
||||||
|
|
||||||
|
Two unit tests in clan_cli/machines/build_test.py cover the CLI fixes;
|
||||||
|
both fail on main.
|
||||||
|
---
|
||||||
|
checks/clan-test-sd-card/default.nix | 196 ++++++++++++++++++
|
||||||
|
checks/clan-test-sd-card/flake.nix | 70 +++++++
|
||||||
|
checks/flake-module.nix | 1 +
|
||||||
|
nixosModules/clanCore/image/default.nix | 1 +
|
||||||
|
.../clanCore/image/sd-card/default.nix | 179 ++++++++++++++++
|
||||||
|
pkgs/clan-cli/clan_cli/machines/build_test.py | 67 +++++-
|
||||||
|
pkgs/clan-cli/clan_lib/machines/build.py | 107 +++++-----
|
||||||
|
7 files changed, 573 insertions(+), 48 deletions(-)
|
||||||
|
create mode 100644 checks/clan-test-sd-card/default.nix
|
||||||
|
create mode 100644 checks/clan-test-sd-card/flake.nix
|
||||||
|
create mode 100644 nixosModules/clanCore/image/sd-card/default.nix
|
||||||
|
|
||||||
|
diff --git a/checks/clan-test-sd-card/default.nix b/checks/clan-test-sd-card/default.nix
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..e692242
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/checks/clan-test-sd-card/default.nix
|
||||||
|
@@ -0,0 +1,196 @@
|
||||||
|
+{
|
||||||
|
+ pkgs,
|
||||||
|
+ lib,
|
||||||
|
+ nixosLib,
|
||||||
|
+ clan-core,
|
||||||
|
+ ...
|
||||||
|
+}:
|
||||||
|
+let
|
||||||
|
+ system = pkgs.stdenv.hostPlatform.system;
|
||||||
|
+ qemu-common = import (pkgs.path + "/nixos/lib/qemu-common.nix") {
|
||||||
|
+ inherit lib;
|
||||||
|
+ inherit (pkgs) stdenv;
|
||||||
|
+ };
|
||||||
|
+in
|
||||||
|
+nixosLib.runTest (
|
||||||
|
+ { config, ... }:
|
||||||
|
+ let
|
||||||
|
+ closureInfo = pkgs.closureInfo {
|
||||||
|
+ rootPaths = [
|
||||||
|
+ config.clan.test.machinesCross.${system}.peer1.config.system.build.images.sd-card.drvPath
|
||||||
|
+ config.clan.test.machinesCross.${system}.peer1.config.clan.core.image.sd-card.addFilesScript
|
||||||
|
+ config.clan.test.machinesCross.${system}.peer1.config.sops.package
|
||||||
|
+ config.clan.test.machinesCross.${system}.peer1.config.sops.gnupg.package
|
||||||
|
+ config.clan.test.systemsFile
|
||||||
|
+ config.clan.test.flakeForSandbox
|
||||||
|
+ ];
|
||||||
|
+ };
|
||||||
|
+ in
|
||||||
|
+ {
|
||||||
|
+ imports = [
|
||||||
|
+ clan-core.modules.nixosTest.clanTest
|
||||||
|
+ ];
|
||||||
|
+
|
||||||
|
+ hostPkgs = pkgs;
|
||||||
|
+
|
||||||
|
+ name = "clan-test-sd-card";
|
||||||
|
+
|
||||||
|
+ clan.test.fromFlake = ./.;
|
||||||
|
+
|
||||||
|
+ # No framework-managed nodes; the SD image VM is created dynamically
|
||||||
|
+ containers = lib.mkForce { };
|
||||||
|
+
|
||||||
|
+ testScript = ''
|
||||||
|
+ import os
|
||||||
|
+ import shutil
|
||||||
|
+ import subprocess
|
||||||
|
+ import tempfile
|
||||||
|
+ import time
|
||||||
|
+ from nixos_test_lib.nix_setup import prepare_test_flake
|
||||||
|
+ from nixos_test_lib.port import find_free_port
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ # We don't have any machines to start but this is needed, as the container
|
||||||
|
+ # driver only sets up the environment correctly for nix build if this
|
||||||
|
+ # function es executed
|
||||||
|
+ start_all()
|
||||||
|
+
|
||||||
|
+ with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
+ flake, _ = prepare_test_flake(temp_dir, "${config.clan.test.flakeForSandbox}", "${closureInfo}")
|
||||||
|
+
|
||||||
|
+ # generate sops keys
|
||||||
|
+ print("Generating vars...")
|
||||||
|
+ subprocess.run(
|
||||||
|
+ ["${
|
||||||
|
+ clan-core.packages.${system}.clan-cli-full
|
||||||
|
+ }/bin/clan", "vars", "keygen", "--flake", str(flake), "--debug"],
|
||||||
|
+ check=True,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ # Test 1: Build the SD image with clan CLI (includes secret injection)
|
||||||
|
+ print("Building SD image with clan CLI...")
|
||||||
|
+ result = subprocess.run(
|
||||||
|
+ ["${
|
||||||
|
+ clan-core.packages.${system}.clan-cli-full
|
||||||
|
+ }/bin/clan", "machines", "build", "peer1", "--format", "sd-card", "--flake", str(flake), "--debug"],
|
||||||
|
+ check=True,
|
||||||
|
+ stdout=subprocess.PIPE,
|
||||||
|
+ text=True,
|
||||||
|
+ )
|
||||||
|
+ # The last non-empty line of stdout is the injected image path
|
||||||
|
+ image_path = result.stdout.strip().splitlines()[-1]
|
||||||
|
+ print(f"Built SD image at: {image_path}")
|
||||||
|
+ assert os.path.isfile(image_path), f"SD image not found at {image_path}"
|
||||||
|
+ assert image_path.endswith(".img.zst"), f"expected a compressed image, got {image_path}"
|
||||||
|
+
|
||||||
|
+ # Test 2: Boot the image and verify secrets via SSH
|
||||||
|
+ # QEMU needs a writable raw disk (the root partition is grown on first boot)
|
||||||
|
+ disk = os.path.join(temp_dir, "disk.img")
|
||||||
|
+ subprocess.run(["${pkgs.zstd}/bin/zstd", "-d", "-q", "-o", disk, "--", image_path], check=True)
|
||||||
|
+
|
||||||
|
+ # The image boots via extlinux.conf, which QEMU cannot load by itself.
|
||||||
|
+ # Do what the bootloader would: read kernel, initrd and cmdline from
|
||||||
|
+ # /boot on the root partition and pass them to QEMU directly.
|
||||||
|
+ root_start = int(subprocess.run(
|
||||||
|
+ ["${pkgs.util-linux}/bin/partx", "--show", "-g", "-o", "START", "--nr", "2", disk],
|
||||||
|
+ check=True, capture_output=True, text=True,
|
||||||
|
+ ).stdout)
|
||||||
|
+ rootfs = f"{disk}?offset={root_start * 512}"
|
||||||
|
+ boot_dir = os.path.join(temp_dir, "boot")
|
||||||
|
+ os.mkdir(boot_dir)
|
||||||
|
+ subprocess.run(
|
||||||
|
+ ["${pkgs.e2fsprogs}/bin/debugfs", "-R", f"rdump /boot {boot_dir}", rootfs],
|
||||||
|
+ check=True, capture_output=True,
|
||||||
|
+ )
|
||||||
|
+ extlinux_dir = os.path.join(boot_dir, "boot", "extlinux")
|
||||||
|
+ entry = {}
|
||||||
|
+ with open(os.path.join(extlinux_dir, "extlinux.conf")) as f:
|
||||||
|
+ for line in f:
|
||||||
|
+ key, _, value = line.strip().partition(" ")
|
||||||
|
+ if key in ("LINUX", "INITRD", "APPEND") and key not in entry:
|
||||||
|
+ entry[key] = value
|
||||||
|
+ kernel = os.path.normpath(os.path.join(extlinux_dir, entry["LINUX"]))
|
||||||
|
+ initrd = os.path.normpath(os.path.join(extlinux_dir, entry["INITRD"]))
|
||||||
|
+ print(f"Booting kernel={kernel} initrd={initrd} append={entry['APPEND']!r}")
|
||||||
|
+
|
||||||
|
+ host_port = find_free_port()
|
||||||
|
+
|
||||||
|
+ # Prepare SSH private key
|
||||||
|
+ ssh_key = os.path.join(temp_dir, "id_ed25519")
|
||||||
|
+ shutil.copy("${../assets/ssh/privkey}", ssh_key)
|
||||||
|
+ os.chmod(ssh_key, 0o600)
|
||||||
|
+
|
||||||
|
+ qemu_cmd = [
|
||||||
|
+ *"${qemu-common.qemuBinary pkgs.qemu_test}".split(),
|
||||||
|
+ "-m", "2048",
|
||||||
|
+ "-nographic",
|
||||||
|
+ "-kernel", kernel,
|
||||||
|
+ "-initrd", initrd,
|
||||||
|
+ "-append", entry["APPEND"],
|
||||||
|
+ "-drive", f"file={disk},format=raw,if=virtio",
|
||||||
|
+ "-netdev", f"user,id=net0,hostfwd=tcp::{host_port}-:22",
|
||||||
|
+ "-device", "virtio-net-pci,netdev=net0",
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ qemu_log = open(os.path.join(temp_dir, "qemu.log"), "wb")
|
||||||
|
+ qemu_proc = subprocess.Popen(
|
||||||
|
+ qemu_cmd,
|
||||||
|
+ stdin=subprocess.DEVNULL,
|
||||||
|
+ stdout=qemu_log,
|
||||||
|
+ stderr=subprocess.STDOUT,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ ssh_cmd = [
|
||||||
|
+ "${pkgs.openssh}/bin/ssh",
|
||||||
|
+ "-o", "UserKnownHostsFile=/dev/null",
|
||||||
|
+ "-o", "StrictHostKeyChecking=no",
|
||||||
|
+ "-o", "ConnectTimeout=3",
|
||||||
|
+ "-p", str(host_port),
|
||||||
|
+ "-i", ssh_key,
|
||||||
|
+ "root@localhost",
|
||||||
|
+ ]
|
||||||
|
+
|
||||||
|
+ # Wait for SSH to become available by trying to connect
|
||||||
|
+ print(f"Waiting for SSH on host port {host_port}...")
|
||||||
|
+ for i in range(300):
|
||||||
|
+ if qemu_proc.poll() is not None:
|
||||||
|
+ qemu_log.close()
|
||||||
|
+ print(open(os.path.join(temp_dir, "qemu.log"), errors="replace").read())
|
||||||
|
+ raise Exception(f"QEMU exited prematurely with code {qemu_proc.returncode}")
|
||||||
|
+ ret = subprocess.run(ssh_cmd + ["true"], capture_output=True)
|
||||||
|
+ if ret.returncode == 0:
|
||||||
|
+ print(f"SSH available after {i + 1} attempts")
|
||||||
|
+ break
|
||||||
|
+ time.sleep(1)
|
||||||
|
+ else:
|
||||||
|
+ qemu_log.close()
|
||||||
|
+ print(open(os.path.join(temp_dir, "qemu.log"), errors="replace").read())
|
||||||
|
+ raise Exception(f"SSH never became available on port {host_port}")
|
||||||
|
+
|
||||||
|
+ # Verify the test-generator secret is available with the expected value
|
||||||
|
+ result = subprocess.run(
|
||||||
|
+ ssh_cmd + ["cat /run/secrets/vars/per-machine/peer1/test-generator/test-secret"],
|
||||||
|
+ check=True,
|
||||||
|
+ capture_output=True,
|
||||||
|
+ text=True,
|
||||||
|
+ )
|
||||||
|
+ actual_secret = result.stdout.strip()
|
||||||
|
+ assert actual_secret == "HongKong", f"Secret mismatch: got {actual_secret!r}, expected 'HongKong'"
|
||||||
|
+
|
||||||
|
+ # The injected age key must be root-only on the root filesystem
|
||||||
|
+ result = subprocess.run(
|
||||||
|
+ ssh_cmd + ["stat -c '%U:%G %a' /var/lib/sops-nix/key.txt"],
|
||||||
|
+ check=True,
|
||||||
|
+ capture_output=True,
|
||||||
|
+ text=True,
|
||||||
|
+ )
|
||||||
|
+ assert result.stdout.strip() == "root:root 600", f"unexpected key.txt ownership/mode: {result.stdout.strip()!r}"
|
||||||
|
+
|
||||||
|
+ print("All SD image tests passed!")
|
||||||
|
+
|
||||||
|
+ qemu_proc.terminate()
|
||||||
|
+ qemu_proc.wait()
|
||||||
|
+ qemu_log.close()
|
||||||
|
+ '';
|
||||||
|
+ }
|
||||||
|
+)
|
||||||
|
diff --git a/checks/clan-test-sd-card/flake.nix b/checks/clan-test-sd-card/flake.nix
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..94a0948
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/checks/clan-test-sd-card/flake.nix
|
||||||
|
@@ -0,0 +1,70 @@
|
||||||
|
+{
|
||||||
|
+ inputs.clan-core.url = "https://__replace__";
|
||||||
|
+ inputs.nixpkgs.url = "https://__replace__";
|
||||||
|
+ inputs.clan-core.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
+ inputs.systems.url = "https://__systems__";
|
||||||
|
+ inputs.systems.flake = false;
|
||||||
|
+
|
||||||
|
+ outputs =
|
||||||
|
+ {
|
||||||
|
+ self,
|
||||||
|
+ clan-core,
|
||||||
|
+ systems,
|
||||||
|
+ ...
|
||||||
|
+ }:
|
||||||
|
+ let
|
||||||
|
+ inherit (clan-core.inputs.nixpkgs) lib;
|
||||||
|
+
|
||||||
|
+ hostSystem = lib.head (import systems);
|
||||||
|
+
|
||||||
|
+ # Usage see: https://clan.lol/docs
|
||||||
|
+ clan = clan-core.lib.clan {
|
||||||
|
+ inherit self;
|
||||||
|
+
|
||||||
|
+ machines.peer1 =
|
||||||
|
+ { modulesPath, ... }:
|
||||||
|
+ {
|
||||||
|
+ nixpkgs.hostPlatform = hostSystem;
|
||||||
|
+
|
||||||
|
+ imports = [
|
||||||
|
+ # Required for the NixOS test driver backdoor (hvc0 shell)
|
||||||
|
+ "${modulesPath}/testing/test-instrumentation.nix"
|
||||||
|
+ ];
|
||||||
|
+
|
||||||
|
+ # The image is booted from a virtio disk in QEMU
|
||||||
|
+ boot.initrd.availableKernelModules = [
|
||||||
|
+ "virtio_pci"
|
||||||
|
+ "virtio_blk"
|
||||||
|
+ ];
|
||||||
|
+
|
||||||
|
+ # Disable vars state version generation
|
||||||
|
+ clan.core.settings.state-version.enable = true;
|
||||||
|
+
|
||||||
|
+ # Enable one secret to be generated
|
||||||
|
+ clan.core.vars.generators.test-generator = {
|
||||||
|
+ files.test-secret.secret = true;
|
||||||
|
+ script = "echo HongKong > $out/test-secret";
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ # Enable SSH for boot verification
|
||||||
|
+ services.openssh.enable = true;
|
||||||
|
+ services.openssh.settings.PermitRootLogin = "yes";
|
||||||
|
+ users.users.root.openssh.authorizedKeys.keys = [
|
||||||
|
+ (builtins.readFile (clan-core + "/checks/assets/ssh/pubkey"))
|
||||||
|
+ ];
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ inventory =
|
||||||
|
+ { ... }:
|
||||||
|
+ {
|
||||||
|
+ meta.name = "foo";
|
||||||
|
+ meta.domain = "foo";
|
||||||
|
+ machines.peer1 = { };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+ in
|
||||||
|
+ {
|
||||||
|
+ # all machines managed by Clan
|
||||||
|
+ inherit (clan.config) nixosConfigurations nixosModules clanInternals;
|
||||||
|
+ };
|
||||||
|
+}
|
||||||
|
diff --git a/checks/flake-module.nix b/checks/flake-module.nix
|
||||||
|
index b6aa02a..5ce0733 100644
|
||||||
|
--- a/checks/flake-module.nix
|
||||||
|
+++ b/checks/flake-module.nix
|
||||||
|
@@ -94,6 +94,7 @@ in
|
||||||
|
service-dummy-test = import ./service-dummy-test nixosTestArgs;
|
||||||
|
service-dummy-test-from-flake = import ./service-dummy-test-from-flake nixosTestArgs;
|
||||||
|
clan-test-iso = import ./clan-test-iso nixosTestArgs;
|
||||||
|
+ clan-test-sd-card = import ./clan-test-sd-card nixosTestArgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
packagesToBuild = lib.removeAttrs self'.packages (
|
||||||
|
diff --git a/nixosModules/clanCore/image/default.nix b/nixosModules/clanCore/image/default.nix
|
||||||
|
index 7dc3b5a..6191385 100644
|
||||||
|
--- a/nixosModules/clanCore/image/default.nix
|
||||||
|
+++ b/nixosModules/clanCore/image/default.nix
|
||||||
|
@@ -2,5 +2,6 @@
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./iso
|
||||||
|
+ ./sd-card
|
||||||
|
];
|
||||||
|
}
|
||||||
|
diff --git a/nixosModules/clanCore/image/sd-card/default.nix b/nixosModules/clanCore/image/sd-card/default.nix
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..2ef9fee
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/nixosModules/clanCore/image/sd-card/default.nix
|
||||||
|
@@ -0,0 +1,179 @@
|
||||||
|
+{
|
||||||
|
+ lib,
|
||||||
|
+ pkgs,
|
||||||
|
+ ...
|
||||||
|
+}:
|
||||||
|
+let
|
||||||
|
+ parseAddFilesArgs = builtins.path {
|
||||||
|
+ path = ../parse-add-files-args.sh;
|
||||||
|
+ name = "parse-add-files-args.sh";
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ # Writes extra files straight into the ext4 root partition of an SD image
|
||||||
|
+ # with debugfs, so no boot-time hook is needed: the files exist on the root
|
||||||
|
+ # filesystem from the first boot. Runs unprivileged and without mounting.
|
||||||
|
+ sdCardAddFilesScript = pkgs.writeShellApplication {
|
||||||
|
+ name = "add-files";
|
||||||
|
+ runtimeInputs = with pkgs; [
|
||||||
|
+ coreutils
|
||||||
|
+ diffutils
|
||||||
|
+ e2fsprogs
|
||||||
|
+ findutils
|
||||||
|
+ util-linux
|
||||||
|
+ zstd
|
||||||
|
+ ];
|
||||||
|
+ extraShellCheckFlags = [ "-x" ];
|
||||||
|
+ text = ''
|
||||||
|
+ # shellcheck source=${parseAddFilesArgs}
|
||||||
|
+ source ${parseAddFilesArgs}
|
||||||
|
+
|
||||||
|
+ # If no extra paths, just copy source to output
|
||||||
|
+ if [[ ''${#extra_paths[@]} -eq 0 ]]; then
|
||||||
|
+ cp -- "$source_image" "$output_image"
|
||||||
|
+ exit 0
|
||||||
|
+ fi
|
||||||
|
+
|
||||||
|
+ # The injection sandbox mounts /tmp as a RAM-backed tmpfs and a raw SD
|
||||||
|
+ # image is several GiB, so stage it next to the output, the only
|
||||||
|
+ # writable on-disk path.
|
||||||
|
+ workdir=$(dirname "$output_image")
|
||||||
|
+ img="$workdir/.$(basename "$output_image").raw"
|
||||||
|
+ cmds=$(mktemp)
|
||||||
|
+ trap 'rm -f -- "$img" "$cmds"' EXIT
|
||||||
|
+
|
||||||
|
+ case "$source_image" in
|
||||||
|
+ *.zst)
|
||||||
|
+ zstd -d -q --no-progress -o "$img" -- "$source_image"
|
||||||
|
+ compress=1
|
||||||
|
+ ;;
|
||||||
|
+ *)
|
||||||
|
+ cp --no-preserve=mode -- "$source_image" "$img"
|
||||||
|
+ compress=0
|
||||||
|
+ ;;
|
||||||
|
+ esac
|
||||||
|
+ chmod u+w -- "$img"
|
||||||
|
+
|
||||||
|
+ # sd-image.nix writes an MBR whose ext4 root is the (only) type-83 partition.
|
||||||
|
+ root_start=""
|
||||||
|
+ while read -r _ start type; do
|
||||||
|
+ if [[ "$type" == "0x83" ]]; then
|
||||||
|
+ root_start=$start
|
||||||
|
+ fi
|
||||||
|
+ done < <(partx --show -g -o NR,START,TYPE -- "$img")
|
||||||
|
+ if [[ -z "$root_start" ]]; then
|
||||||
|
+ echo "No Linux (type 83) root partition found in $source_image" >&2
|
||||||
|
+ exit 1
|
||||||
|
+ fi
|
||||||
|
+ # e2fsprogs opens a partition inside a disk image via "<file>?offset=<bytes>".
|
||||||
|
+ rootfs="$img?offset=$((root_start * 512))"
|
||||||
|
+
|
||||||
|
+ # Build a debugfs command script. debugfs has neither mkdir -p nor a
|
||||||
|
+ # recursive write, so directories are created ancestor-first ("already
|
||||||
|
+ # exists" errors are harmless). write copies the source mode; ownership
|
||||||
|
+ # is forced to root since the script runs as an unprivileged user.
|
||||||
|
+ declare -A made_dirs=()
|
||||||
|
+ written=()
|
||||||
|
+ emit_mkdir_p() {
|
||||||
|
+ local dir="$1" path="" part
|
||||||
|
+ IFS=/ read -ra parts <<< "$dir"
|
||||||
|
+ for part in "''${parts[@]}"; do
|
||||||
|
+ [[ -z "$part" ]] && continue
|
||||||
|
+ path="$path/$part"
|
||||||
|
+ if [[ -z "''${made_dirs[$path]:-}" ]]; then
|
||||||
|
+ echo "mkdir \"$path\""
|
||||||
|
+ made_dirs[$path]=1
|
||||||
|
+ fi
|
||||||
|
+ done
|
||||||
|
+ }
|
||||||
|
+ emit_write() {
|
||||||
|
+ local src="$1" dest="$2"
|
||||||
|
+ echo "write \"$src\" \"$dest\""
|
||||||
|
+ echo "sif \"$dest\" uid 0"
|
||||||
|
+ echo "sif \"$dest\" gid 0"
|
||||||
|
+ written+=("$src" "$dest")
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for ((i = 0; i < ''${#extra_paths[@]}; i += 2)); do
|
||||||
|
+ local_src="''${extra_paths[i]}"
|
||||||
|
+ # Normalize to an absolute path without trailing slash; "/" becomes "".
|
||||||
|
+ target="/''${extra_paths[i+1]#/}"
|
||||||
|
+ target="''${target%/}"
|
||||||
|
+
|
||||||
|
+ if [[ -d "$local_src" ]]; then
|
||||||
|
+ emit_mkdir_p "$target"
|
||||||
|
+ while IFS= read -r -d "" entry; do
|
||||||
|
+ dest="$target/''${entry#"$local_src"/}"
|
||||||
|
+ if [[ -d "$entry" ]]; then
|
||||||
|
+ emit_mkdir_p "$dest"
|
||||||
|
+ elif [[ -f "$entry" ]]; then
|
||||||
|
+ emit_write "$entry" "$dest"
|
||||||
|
+ else
|
||||||
|
+ echo "Unsupported file type (only regular files and directories): $entry" >&2
|
||||||
|
+ exit 1
|
||||||
|
+ fi
|
||||||
|
+ done < <(
|
||||||
|
+ # Never treat our own staging/output directory as input, even if
|
||||||
|
+ # the caller placed it inside an extra path.
|
||||||
|
+ find "$local_src" -mindepth 1 -path "$workdir" -prune -o -print0
|
||||||
|
+ )
|
||||||
|
+ elif [[ -f "$local_src" ]]; then
|
||||||
|
+ emit_mkdir_p "$(dirname "$target")"
|
||||||
|
+ emit_write "$local_src" "$target"
|
||||||
|
+ else
|
||||||
|
+ echo "Extra path does not exist: $local_src" >&2
|
||||||
|
+ exit 1
|
||||||
|
+ fi
|
||||||
|
+ done > "$cmds"
|
||||||
|
+
|
||||||
|
+ debugfs -w -f "$cmds" "$rootfs"
|
||||||
|
+
|
||||||
|
+ # debugfs exits 0 even when commands fail: verify every file landed
|
||||||
|
+ # intact and the filesystem is still consistent.
|
||||||
|
+ for ((i = 0; i < ''${#written[@]}; i += 2)); do
|
||||||
|
+ if ! debugfs -R "cat \"''${written[i+1]}\"" "$rootfs" 2>/dev/null | cmp -s -- - "''${written[i]}"; then
|
||||||
|
+ echo "Failed to write ''${written[i+1]} into the root filesystem" >&2
|
||||||
|
+ exit 1
|
||||||
|
+ fi
|
||||||
|
+ done
|
||||||
|
+ if ! fsck_output=$(EXT2FS_NO_MTAB_OK=yes e2fsck -fn "$rootfs" 2>&1); then
|
||||||
|
+ echo "Root filesystem is inconsistent after injection:" >&2
|
||||||
|
+ echo "$fsck_output" >&2
|
||||||
|
+ exit 1
|
||||||
|
+ fi
|
||||||
|
+
|
||||||
|
+ if [[ "$compress" == 1 ]]; then
|
||||||
|
+ zstd -T0 -q --no-progress --rm -o "$output_image" -- "$img"
|
||||||
|
+ else
|
||||||
|
+ mv -- "$img" "$output_image"
|
||||||
|
+ fi
|
||||||
|
+ '';
|
||||||
|
+ };
|
||||||
|
+in
|
||||||
|
+{
|
||||||
|
+ options.clan.core.image = {
|
||||||
|
+ sd-card.addFilesScript = lib.mkOption {
|
||||||
|
+ type = lib.types.package;
|
||||||
|
+ default = sdCardAddFilesScript;
|
||||||
|
+ defaultText = lib.literalExpression "pkgs.writeShellApplication { ... }";
|
||||||
|
+ description = ''
|
||||||
|
+ Script to inject extra files into an SD card image
|
||||||
|
+ (`clan machines build <machine> --format sd-card`).
|
||||||
|
+
|
||||||
|
+ Files are written directly into the ext4 root partition, so they are
|
||||||
|
+ present from the first boot without any initrd hook. Both compressed
|
||||||
|
+ (`.img.zst`, the `sdImage.compressImage` default) and raw `.img`
|
||||||
|
+ images are supported; the output keeps the source's format.
|
||||||
|
+
|
||||||
|
+ Source `parse-add-files-args.sh` to get standardized argument parsing.
|
||||||
|
+ After sourcing, `$source_image`, `$output_image`, and `$extra_paths`
|
||||||
|
+ are available.
|
||||||
|
+
|
||||||
|
+ Accepts the following arguments:
|
||||||
|
+ - --source `<path>`: Path to the original built image (read-only)
|
||||||
|
+ - --output `<path>`: Path for the modified output image
|
||||||
|
+ - --extra-path `<local-source>` `<target-path>`: Repeatable, maps a local
|
||||||
|
+ file or directory to a target filesystem path inside the image
|
||||||
|
+ '';
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+}
|
||||||
|
diff --git a/pkgs/clan-cli/clan_cli/machines/build_test.py b/pkgs/clan-cli/clan_cli/machines/build_test.py
|
||||||
|
index ad0caf0..b5642a0 100644
|
||||||
|
--- a/pkgs/clan-cli/clan_cli/machines/build_test.py
|
||||||
|
+++ b/pkgs/clan-cli/clan_cli/machines/build_test.py
|
||||||
|
@@ -1,8 +1,9 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
-from clan_lib.errors import ClanError
|
||||||
|
+from clan_lib.errors import ClanError, CmdOut
|
||||||
|
from clan_lib.flake import Flake
|
||||||
|
+from clan_lib.machines import build as build_lib
|
||||||
|
from clan_lib.machines.build import BuildOptions, build_machine
|
||||||
|
from clan_lib.machines.machines import Machine
|
||||||
|
|
||||||
|
@@ -131,3 +132,67 @@ def test_build_machine_without_hostplatform_raises_friendly_error(
|
||||||
|
assert "nixpkgs.hostPlatform" in msg
|
||||||
|
assert "--system" in msg
|
||||||
|
assert "jon" in msg
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_build_machine_skips_injection_for_format_without_add_files_script(
|
||||||
|
+ tmp_path: Path,
|
||||||
|
+ monkeypatch: pytest.MonkeyPatch,
|
||||||
|
+) -> None:
|
||||||
|
+ """Formats without clan.core.image.<format>.addFilesScript (e.g. sd-card)
|
||||||
|
+ must build successfully and return the raw nix output, not fail trying
|
||||||
|
+ to locate an image file for injection.
|
||||||
|
+ """
|
||||||
|
+ build_dir = tmp_path / "out"
|
||||||
|
+ (build_dir / "sd-image").mkdir(parents=True)
|
||||||
|
+ (build_dir / "sd-image" / "nixos.img.zst").touch()
|
||||||
|
+
|
||||||
|
+ def fake_run(cmd: list[str], *_args: object, **_kwargs: object) -> CmdOut:
|
||||||
|
+ return CmdOut(
|
||||||
|
+ stdout=f"{build_dir}\n",
|
||||||
|
+ stderr="",
|
||||||
|
+ env=None,
|
||||||
|
+ cwd=tmp_path,
|
||||||
|
+ command_list=cmd,
|
||||||
|
+ returncode=0,
|
||||||
|
+ msg=None,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def fake_select_machine(_self: Flake, _machine: str, selector: str) -> object:
|
||||||
|
+ assert selector == "config.clan.core.image.?sd-card.addFilesScript"
|
||||||
|
+ return {}
|
||||||
|
+
|
||||||
|
+ def no_secrets(*_args: object, **_kwargs: object) -> object:
|
||||||
|
+ msg = "secrets must not be generated for a format without addFilesScript"
|
||||||
|
+ raise AssertionError(msg)
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(build_lib, "get_build_target", lambda *_a, **_k: "flake#x")
|
||||||
|
+ monkeypatch.setattr(build_lib, "run", fake_run)
|
||||||
|
+ monkeypatch.setattr(build_lib, "get_machine_generators", no_secrets)
|
||||||
|
+ monkeypatch.setattr(Flake, "select_machine", fake_select_machine)
|
||||||
|
+
|
||||||
|
+ machine = Machine(name="jon", flake=Flake(str(tmp_path)))
|
||||||
|
+ result = build_machine(machine, BuildOptions(format="sd-card", no_link=True))
|
||||||
|
+
|
||||||
|
+ assert result.build_path == build_dir
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_find_image_file_uses_image_filepath(
|
||||||
|
+ tmp_path: Path,
|
||||||
|
+ monkeypatch: pytest.MonkeyPatch,
|
||||||
|
+) -> None:
|
||||||
|
+ """The image lives at config.image.filePath inside the build output,
|
||||||
|
+ which is not necessarily a directory named after the format
|
||||||
|
+ (sd-card -> sd-image/...).
|
||||||
|
+ """
|
||||||
|
+ image = tmp_path / "sd-image" / "nixos-image-sd-card-x86_64-linux.img.zst"
|
||||||
|
+ image.parent.mkdir()
|
||||||
|
+ image.touch()
|
||||||
|
+
|
||||||
|
+ def fake_select_machine(_self: Flake, _machine: str, selector: str) -> str:
|
||||||
|
+ assert selector == "config.system.build.images.sd-card.passthru.filePath"
|
||||||
|
+ return "sd-image/nixos-image-sd-card-x86_64-linux.img.zst"
|
||||||
|
+
|
||||||
|
+ monkeypatch.setattr(Flake, "select_machine", fake_select_machine)
|
||||||
|
+ machine = Machine(name="jon", flake=Flake(str(tmp_path)))
|
||||||
|
+
|
||||||
|
+ assert build_lib._find_image_file(machine, tmp_path, "sd-card") == image
|
||||||
|
diff --git a/pkgs/clan-cli/clan_lib/machines/build.py b/pkgs/clan-cli/clan_lib/machines/build.py
|
||||||
|
index 00e2b16..d9ed5a1 100644
|
||||||
|
--- a/pkgs/clan-cli/clan_lib/machines/build.py
|
||||||
|
+++ b/pkgs/clan-cli/clan_lib/machines/build.py
|
||||||
|
@@ -98,32 +98,42 @@ def _hostplatform_unset_hint(machine: Machine, build_format: str) -> str:
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def _is_image_format(build_format: str) -> bool:
|
||||||
|
- """Check if the build format is an image format that supports secret injection."""
|
||||||
|
- return build_format not in ("toplevel")
|
||||||
|
+def _image_add_files_script(machine: Machine, build_format: str) -> str | None:
|
||||||
|
+ """Return the format's addFilesScript store path.
|
||||||
|
+
|
||||||
|
+ None when clan has no file-injection support for this format
|
||||||
|
+ (i.e. no `clan.core.image.<format>.addFilesScript` option), in which
|
||||||
|
+ case secret injection must be skipped rather than fail.
|
||||||
|
+ """
|
||||||
|
+ result = machine.flake.select_machine(
|
||||||
|
+ machine.name,
|
||||||
|
+ f"config.clan.core.image.?{build_format}.addFilesScript",
|
||||||
|
+ )
|
||||||
|
+ return result.get(build_format)
|
||||||
|
|
||||||
|
|
||||||
|
-def _find_image_file(build_dir: Path, build_format: str) -> Path:
|
||||||
|
- """Find the actual image file within a build output directory.
|
||||||
|
+def _find_image_file(machine: Machine, build_dir: Path, build_format: str) -> Path:
|
||||||
|
+ """Resolve the image file within an image build output directory.
|
||||||
|
|
||||||
|
- Image builds output a directory (e.g., /nix/store/...-x86_64-linux.iso/)
|
||||||
|
- containing the image file in a subdirectory named after the format
|
||||||
|
- (e.g., iso/nixos-26.05-x86_64-linux.iso).
|
||||||
|
+ Image builds output a directory whose layout is format-specific
|
||||||
|
+ (iso/nixos-….iso, sd-image/nixos-….img.zst, …). nixpkgs exposes the
|
||||||
|
+ relative location as `config.image.filePath`, attached to the image
|
||||||
|
+ derivation as `passthru.filePath`.
|
||||||
|
"""
|
||||||
|
- format_dir = build_dir / build_format
|
||||||
|
- if not format_dir.is_dir():
|
||||||
|
- msg = f"Expected directory {format_dir} not found in build output"
|
||||||
|
- raise ClanError(msg)
|
||||||
|
- files = [f for f in format_dir.iterdir() if f.is_file()]
|
||||||
|
- if len(files) != 1:
|
||||||
|
- msg = f"Expected exactly one file in {format_dir}, found {len(files)}"
|
||||||
|
+ file_path: str = machine.flake.select_machine(
|
||||||
|
+ machine.name,
|
||||||
|
+ f"config.system.build.images.{build_format}.passthru.filePath",
|
||||||
|
+ )
|
||||||
|
+ image_file = build_dir / file_path
|
||||||
|
+ if not image_file.is_file():
|
||||||
|
+ msg = f"Expected image file {image_file} not found in build output"
|
||||||
|
raise ClanError(msg)
|
||||||
|
- return files[0]
|
||||||
|
+ return image_file
|
||||||
|
|
||||||
|
|
||||||
|
def _inject_secrets_into_image(
|
||||||
|
machine: Machine,
|
||||||
|
- build_format: str,
|
||||||
|
+ add_files_script: str,
|
||||||
|
source_image_path: Path,
|
||||||
|
output_path: Path,
|
||||||
|
*,
|
||||||
|
@@ -137,7 +147,9 @@ def _inject_secrets_into_image(
|
||||||
|
secrets_target_dir = machine.secret_vars_store.get_upload_directory(machine.name)
|
||||||
|
|
||||||
|
with TemporaryDirectory(prefix="clan-build-secrets-") as tmpdir:
|
||||||
|
- base_dir = Path(tmpdir).resolve()
|
||||||
|
+ # The tree mapped to "/" inside the image lives in its own subdir so
|
||||||
|
+ # the format script never sees its own output as input.
|
||||||
|
+ base_dir = Path(tmpdir).resolve() / "root"
|
||||||
|
upload_dir = base_dir / secrets_target_dir.lstrip("/")
|
||||||
|
upload_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
@@ -153,12 +165,6 @@ def _inject_secrets_into_image(
|
||||||
|
if not any(upload_dir.iterdir()):
|
||||||
|
return False
|
||||||
|
|
||||||
|
- # Get the addFilesScript path from the NixOS config
|
||||||
|
- script_path = machine.flake.select_machine(
|
||||||
|
- machine.name,
|
||||||
|
- f"config.clan.core.image.{build_format}.addFilesScript",
|
||||||
|
- )
|
||||||
|
-
|
||||||
|
# Remove stale output from previous runs (xorriso refuses to
|
||||||
|
# write to an existing non-empty file when indev != outdev)
|
||||||
|
output = Path(output_path)
|
||||||
|
@@ -167,12 +173,12 @@ def _inject_secrets_into_image(
|
||||||
|
|
||||||
|
# Write the image into the tmpdir so the sandbox only needs
|
||||||
|
# write access to the temp directory, not the final destination.
|
||||||
|
- sandbox_output = base_dir / "output" / output.name
|
||||||
|
+ sandbox_output = base_dir.parent / "output" / output.name
|
||||||
|
sandbox_output.parent.mkdir(parents=True)
|
||||||
|
|
||||||
|
# Execute the addFilesScript; clean up partial output on failure
|
||||||
|
inject_cmd: list[str] = [
|
||||||
|
- f"{script_path}/bin/add-files",
|
||||||
|
+ f"{add_files_script}/bin/add-files",
|
||||||
|
"--source",
|
||||||
|
str(source_image_path),
|
||||||
|
"--output",
|
||||||
|
@@ -265,33 +271,40 @@ def build_machine(
|
||||||
|
if build_path and (store := nix_test_store()):
|
||||||
|
build_path = store / str(build_path).lstrip("/")
|
||||||
|
|
||||||
|
- # Post-build secret injection for image formats
|
||||||
|
+ # Post-build secret injection for image formats that support it
|
||||||
|
if (
|
||||||
|
build_path
|
||||||
|
- and _is_image_format(options.format)
|
||||||
|
+ and options.format != "toplevel"
|
||||||
|
and not options.no_secrets
|
||||||
|
and not options.dry_run
|
||||||
|
):
|
||||||
|
- # build_path is a directory; resolve the actual image file inside
|
||||||
|
- source_file = _find_image_file(build_path, options.format)
|
||||||
|
- # Output to a file next to the symlink, named after the source file
|
||||||
|
- output_file = Path.cwd() / source_file.name
|
||||||
|
-
|
||||||
|
- secrets_injected = _inject_secrets_into_image(
|
||||||
|
- machine=machine,
|
||||||
|
- build_format=options.format,
|
||||||
|
- source_image_path=source_file,
|
||||||
|
- output_path=output_file,
|
||||||
|
- use_sandbox=options.use_sandbox,
|
||||||
|
- )
|
||||||
|
- if secrets_injected:
|
||||||
|
- log.warning(
|
||||||
|
- "This image contains secret key material "
|
||||||
|
- "(age private key). Treat this image as a sensitive "
|
||||||
|
- "artifact. Do not store it on shared or public media "
|
||||||
|
- "without encryption. Securely erase after use."
|
||||||
|
+ add_files_script = _image_add_files_script(machine, options.format)
|
||||||
|
+ if add_files_script is None:
|
||||||
|
+ log.info(
|
||||||
|
+ f"Image format '{options.format}' has no addFilesScript; "
|
||||||
|
+ "skipping secret injection"
|
||||||
|
)
|
||||||
|
- build_path = output_file
|
||||||
|
+ else:
|
||||||
|
+ # build_path is a directory; resolve the actual image file inside
|
||||||
|
+ source_file = _find_image_file(machine, build_path, options.format)
|
||||||
|
+ # Output to a file next to the symlink, named after the source file
|
||||||
|
+ output_file = Path.cwd() / source_file.name
|
||||||
|
+
|
||||||
|
+ secrets_injected = _inject_secrets_into_image(
|
||||||
|
+ machine=machine,
|
||||||
|
+ add_files_script=add_files_script,
|
||||||
|
+ source_image_path=source_file,
|
||||||
|
+ output_path=output_file,
|
||||||
|
+ use_sandbox=options.use_sandbox,
|
||||||
|
+ )
|
||||||
|
+ if secrets_injected:
|
||||||
|
+ log.warning(
|
||||||
|
+ "This image contains secret key material "
|
||||||
|
+ "(age private key). Treat this image as a sensitive "
|
||||||
|
+ "artifact. Do not store it on shared or public media "
|
||||||
|
+ "without encryption. Securely erase after use."
|
||||||
|
+ )
|
||||||
|
+ build_path = output_file
|
||||||
|
|
||||||
|
return BuildResult(
|
||||||
|
machine_name=machine.name,
|
||||||
|
--
|
||||||
|
2.54.0
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ let
|
|||||||
domain = "git.b4l.co.th";
|
domain = "git.b4l.co.th";
|
||||||
owner = "newedge";
|
owner = "newedge";
|
||||||
repo = "default_thai_company";
|
repo = "default_thai_company";
|
||||||
rev = "c8fb98374989a22d0c907aa44877f247391a49ac";
|
rev = "68f08171252099920d52f5c47800d65ddf4689b3";
|
||||||
sha256 = "sha256-c+pqds4l3Laldgf3+Rj2Ug693kFJYDB1j4a15j4U5SY=";
|
sha256 = "sha256-9UomP8G5K+nkAMCfn+gXbBNivLlDo0c/36FYZgHm2Cw=";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
buildPythonPackage (finalAttrs: {
|
buildPythonPackage (finalAttrs: {
|
||||||
|
|||||||
@@ -7,17 +7,18 @@
|
|||||||
let
|
let
|
||||||
repoSrc = fetchgit {
|
repoSrc = fetchgit {
|
||||||
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
||||||
rev = "895092dea1da99ca1fa3c5c6d805384e579d417e";
|
rev = "6f8c8d7dfaf5a0c1eb2077de1d6fb35ceaf3d4ec";
|
||||||
hash = "sha256-ffbwQVQ1lL8XpubsGX6FTbJOjz1mbqCn+AKvR7xJhRw=";
|
hash = "sha256-2mCdn8xGjWZrANclctGTmxQhkNc43VzlzMTVwVIFJcM=";
|
||||||
};
|
};
|
||||||
src = "${repoSrc}/upload-large-file";
|
src = "${repoSrc}/upload-large-file";
|
||||||
in
|
in
|
||||||
buildNpmPackage {
|
buildNpmPackage {
|
||||||
pname = "gtcm-file-uploader";
|
pname = "gtcm-file-uploader";
|
||||||
version = "1.1.2";
|
version = "1.0.0";
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
nodejs
|
nodejs
|
||||||
|
breakpointHook
|
||||||
];
|
];
|
||||||
|
|
||||||
inherit src;
|
inherit src;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
final: prev: {
|
final: prev: {
|
||||||
think-gtcm = final.callPackage ./think-gtcm.nix { php = final.php83; };
|
think-gtcm = final.callPackage ./think-gtcm.nix { };
|
||||||
think-backend-gtcm = final.callPackage ./think-backend-gtcm.nix { php = final.php83; };
|
think-backend-gtcm = final.callPackage ./think-backend-gtcm.nix { php = final.php83; };
|
||||||
gtcm-file-uploader = final.callPackage ./gtcm-file-uploader.nix { };
|
gtcm-file-uploader = final.callPackage ./gtcm-file-uploader.nix { };
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,14 @@
|
|||||||
let
|
let
|
||||||
repoSrc = fetchgit {
|
repoSrc = fetchgit {
|
||||||
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
||||||
rev = "895092dea1da99ca1fa3c5c6d805384e579d417e";
|
rev = "7c17aa78436538241c09fc7d633904d3c063011e";
|
||||||
hash = "sha256-ffbwQVQ1lL8XpubsGX6FTbJOjz1mbqCn+AKvR7xJhRw=";
|
hash = "sha256-GDx0+PmuCXC+UPtsvsocCZQiTPcnOZEzJI17sxrVv7Q=";
|
||||||
};
|
};
|
||||||
src = "${repoSrc}/think-backend.greaterchiangmai.com";
|
src = "${repoSrc}/think-backend.greaterchiangmai.com";
|
||||||
in
|
in
|
||||||
php.buildComposerProject2 (finalAttrs: {
|
php.buildComposerProject2 (finalAttrs: {
|
||||||
pname = "think-backend-gtcm";
|
pname = "think-backend-gtcm";
|
||||||
version = "1.1.2";
|
version = "1.0.0";
|
||||||
|
|
||||||
inherit src;
|
inherit src;
|
||||||
|
|
||||||
@@ -32,5 +32,5 @@ php.buildComposerProject2 (finalAttrs: {
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
composerStrictValidation = false;
|
composerStrictValidation = false;
|
||||||
vendorHash = "sha256-1cY33xOF0f1qWkZKtJSjOYReb9vLQRnLZuF+GdVA7xU=";
|
vendorHash = "sha256-wGfbprSDULBje1s5y3+ZiU/nCwYGDEULobZzyzGZ9bQ=";
|
||||||
})
|
})
|
||||||
+4
-4
@@ -6,14 +6,14 @@
|
|||||||
let
|
let
|
||||||
repoSrc = fetchgit {
|
repoSrc = fetchgit {
|
||||||
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
url = "https://git.b4l.co.th/newedge/think-greaterchiangmai";
|
||||||
rev = "895092dea1da99ca1fa3c5c6d805384e579d417e";
|
rev = "7c17aa78436538241c09fc7d633904d3c063011e";
|
||||||
hash = "sha256-ffbwQVQ1lL8XpubsGX6FTbJOjz1mbqCn+AKvR7xJhRw=";
|
hash = "sha256-GDx0+PmuCXC+UPtsvsocCZQiTPcnOZEzJI17sxrVv7Q=";
|
||||||
};
|
};
|
||||||
src = "${repoSrc}/think.greaterchiangmai.com";
|
src = "${repoSrc}/think.greaterchiangmai.com";
|
||||||
in
|
in
|
||||||
php.buildComposerProject2 (finalAttrs: {
|
php.buildComposerProject2 (finalAttrs: {
|
||||||
pname = "think-gtcm";
|
pname = "think-gtcm";
|
||||||
version = "1.1.2";
|
version = "1.0.0";
|
||||||
|
|
||||||
inherit src;
|
inherit src;
|
||||||
|
|
||||||
@@ -32,5 +32,5 @@ php.buildComposerProject2 (finalAttrs: {
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
composerStrictValidation = false;
|
composerStrictValidation = false;
|
||||||
vendorHash = "sha256-rEE1C4uYNoVq5wiCooNnjj6uIQmSel7u9khAlM3hopk=";
|
vendorHash = "sha256-b8+AKUmjQiOdV8UC9GYfJzAHFs9+FRSH91YsxKt0rDA=";
|
||||||
})
|
})
|
||||||
@@ -9,7 +9,15 @@
|
|||||||
devshells.default = {
|
devshells.default = {
|
||||||
devshell = {
|
devshell = {
|
||||||
packages = [
|
packages = [
|
||||||
inputs.clan-core.packages.${system}.clan-cli
|
# Local fix until upstream lands (patches/upstream-sd-card-secret-injection.patch):
|
||||||
|
# `clan machines build --format sd-card` resolves the image via
|
||||||
|
# image.filePath, skips secret injection for formats without
|
||||||
|
# clan.core.image.<format>.addFilesScript, and stages the
|
||||||
|
# injected tree apart from the sandbox output. The sd-card
|
||||||
|
# addFilesScript itself is modules/nixos/sd-card-image.nix.
|
||||||
|
(inputs.clan-core.packages.${system}.clan-cli.overrideAttrs (old: {
|
||||||
|
patches = (old.patches or [ ]) ++ [ ./patches/clan-cli-image-filepath.patch ];
|
||||||
|
}))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/groups/admins
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/machines/adhil
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:HT4hSTyFNaZlSVG5wjxFE6xULu+Cf+hmMi+GJ+YWXJrvNw5bYHaEguJ3Sbq7u+BAYLG/uPVpu/H83VXG9IY6jRKWsClXNmzqimZMSIo6KiIcNW6+czCkk1UTUsTGVi8bQRBiIOGMtMp+RYn3sJ0AxlZx3GrtRq4a/XAlvCrHsw7yxYT0Cs6qJxtXbM2iMUbcfr/lfolDrqjNmhkS5TulY4LzKIeDD6VHkCYOlkIW/m+x6wSquEX6RnPGEuvcvBXte6EvUi692qxjNPRrSMu4tRmVUSr55CY63QsNsEzo1cRsU8+jIGIarN9uxFMQaVBGXQAQNnZgFN3IUrs/sgWYIgCdFIk7Xa8WPra14OxO,iv:PxJrjt1dpL6iHALcvYaqkBybcSvCK9qMEZE3MWjixBQ=,tag:mL//WdhB8SVXp/M3Jxb0HQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqTWViMC9uVHl4QitxZXV4\nWFFtV253NTBBSnJjazJ0UXd1Z3kzaTYvN3hZCnNyRjZaOU02TU5hbUxSVGdlQmlD\ndVZnUDVFSFMzTGg4Q2FIMG5XZHAvNFEKLS0tIHRUMURnYlhFS3RBaXVXaEVyQ21S\nMElwbTBUOVR1dTV2a2ZHVWV4czQwUWMKgQmIfaVSTRr9yytEgEiTQVp4jCREXLQ7\nlOfBUK02hqN26rkVEVu4YcCttx9/nH7Dwc+wc/nxXXDWYLIcwpdMLA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17d4qt0n9edq57tgcqyk8eu5mrendl59yt6z2y3a4vkq7el8krqtq6lq28g"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWYmt1ZElyUjJsclJlZXBY\nWXlXNWVpMUZPZmczaWs2SWFLdzkyT09neEc0CnplRlZ5VS9ZZk5ZODNoNy8yVUtm\ncnQ3RHVtRGVNYk1xSFg1dFRta2pBZTQKLS0tIFBqZ3hwWTJnYkhpZVZUTElIcmto\nc1A1NHdnSFRzRkdGQ3JLYVg2UzF5SzQK4bCCxZRiMSXTkUNOJeuCUcGwz0xoYr7s\nkNVMHSm74UOZG9BowimHiTb9n280ViCaGhZfvKIWkQLRdsYGiUxH5g==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1dytdeqtct0jy3vvmqvapgqjf3v9p486jjcpx60gfxwgrptgyyqrs0lj9vc"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIHZBRlhQWGwv\nQkVLendPcXExRzBXaG9ieDJycWRXMXhOWDN0K2RWSFoyVFkgQVEgd0l6SlBCVW1v\neDl5N0lHYmVrajZzVzVTYjd4WVdNVjZoZVlkUEtmVXlMdyBIdmtoczF0dmhxL1py\nS1hsc0ErckdoVVB0bE02RGdCNW5raERXT1ZJMTkzNjRGNHd2T3ZSTVYrKzZlbEll\nVTZKc2JVN1B2SzRuNkpaekhwSWJQaFZVQQpXUVE4eGJxUTFNYTZSejNoS2tJK1Nw\nUUkzUm44T2l2RjFXTEpaM2w0MkdFCi0tLSA3UElXSlVmZ21nZHUwYUlPU09yZkpi\nc2VSaXo4eVBKOFZ2L3lpVDNxMytZCi5Uz/e8luflh7aUmsDRIo6SIUnvRslFwLzw\nbb1zehkiEHPOdlsccqLNy3jWHT5j26O1/wJgnyUNF+EHlH0SdrU=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpf43tgcfjm048lsqskvq34w2t4uvrm5qy6m2eg6zjj82ctca8wctgpczxvj0q4y6337uhvsxdh5j86k9h9ymautpvv2759ucwnef75ez7pa7fpkddklp40mxk2tedsp74359g0kefn5rsq0x0yss6cu4yd0h06up0rp08t6yc4l0hfa9y8jn5fkx6nk0hjhz06ykwv0fyxe7z42q683jy0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIHNMTm44d1Rh\naGZPM29DOSs2THkxeEoxUUZ1WkxyMUFiMmFWUEZXZm1KU3MgQVEgZlFBRFNyWkNS\nRDJQaUJ3WWowTEcwaTk0Z0tiRHpQNmhkMTJZUlZHbHlEMCBtMzE5bzVQb1JmZkpy\nY0orbEs2RzBSVXA3UmZYZkRRanNnc2pHSnJjK1pLVmZOWGR4d0IwZjh1YXlkakRx\nTm5EMXR3ZjNHWGl0Ty8rcHpQbkcxRTZmZwpObkliM0s3aUI4UnI1Y1J0TEtRQjh4\nWDlBd3pjNlZGSklVTHNUYnRFV1RZCi0tLSAwT2N4ejBLUjlOSU03QmlMRmF2aG5v\nYy81VzB6b0UzTCtBbmNSUFM4UWZZCggMWJpWYEWTe4Am4el4VtIwagRb3uu0qWky\nsdh9OJ9uVoRtUKWdLFKfT4o5eWTj9zP0C/mMuSjpelwo2iRcQ1s=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqprw0vfpc8wzsu78quc777kmee54ln6nnsjrnrhl7nr33eh4kvkksqp05qqxj4kgfzrmrugrsvg7skx6ghh3q9xc0x0agthtkvy25d9eq7eklta5wf7s30hexkuyl5546rdz9ffa5tawlp5yweqkgccntw0ny540n2am3cqw3luhxkfmrp63kwr6mwplhr9u26wll48x0n3k5f60c7hg9a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIHVJMmhGZC85\nT1NQdXBHNXQydFRoclNQUURRVnJjYmlSR3pXTXhWNTVFUmsgQVEgZUh4S2o1R2RE\nV0svbnErTmxodXE2bUxJK3lla2lvajB3OXVwZHJtcHU5dyBKU3I4WFJnK3BOQXl0\nZDh5NTB5ZFlteFJIM2FVTVp5cmNyRXZqL1V0c0l6L0tSb0c5ZU9jbmRXMTIwb2c1\nQjZsREgzWUxaOXBUd2pQK2NncDZuUmZkZwpNS2FlMEdxNjAyZE1hQVZVVUtPL1Jl\nTE5CTXh6TTNKNlI5Q3BtZ01OdnJrCi0tLSBwVERvMnhEYWdwcjE1WFQ2dldqTW5r\ndTVOdzI4TWsrYUdOQ2laOUk4cUVFCi3++2i2ULNWHy6EbUAIVfQo3be4srgMuzMR\nlUWlzlsHmZksrvdpfdWM72i5Hj7hvgIZAyVroPijEc8PXyORm2s=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpyewum3q8dfcumfgec8nn958aec9f4q9aqy0k06kw5kq27d6fdqdgp0p7y4ru3n5xk90u747xevxa2af3v37e85j9g3axrmw5hdwdfh0wz22hut5vrafxsx26a7vh8fjwkymz3ramfgvvu4detztu075kmpr8l9ydqda0rnjwatdwmfgswg849p37astvld98s3nleeq575azlwc2hhpuh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCUHpuc2JjcjFucVpjc0lq\nN1BHclRzV1IzanZrcmdoMFp5UEJ0WGo4MUdZClBOczBCNTZzRitCdmJwZ0RnOE5w\nS2t6aGNhaGJUbXdjYUJNUS83c1FsRGsKLS0tIEEybm9Sc21HOFZLWHNhRml1ZFZF\ndjdiZVo5OElHdVg1L2FnK3NrQzROMXMKHfKVsiisagg77l81ycyfHarJhRaaypOO\nsKbgOWXr33j7D+O9pPXUNlPgofsZTJC10MHUlnTebv74NZUnOIK89g==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5VVp2eXlSd0l3Y2ZJejBq\nMDhEZGRMOUZxTTBLbG1CUDVZTkR0SzEvdVVzCk5BYk5kVENVNE5yMXNmSXQvaFlu\nbFlHZ0s3cExPOVhUUTFWdm56U21JcmcKLS0tIHlySmJjMzJwN3lFamZkSS9SbnZ6\nWkhRM1ZBNmsxcjI3UDEyYS8wZ3p4TzQKTqU63FMjj3HywYeENZpVdzthhlOAhf7M\naQ33Cb+EQasD9Osb5gF/ZKjDvhLTe0LemV8CVG2eb6DwyLpDgOYcIA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sg0rvgyetdcqw7j2x983fh69kdkvqsngpe5x36e5920qa7fze3cqhj4wgx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCckJ4bjFkUzY4RFRxMHln\nU0NLUWg5YlJ1cXJEbnN1TW93bmNJMjB4emhrCnZCR242ak43V0wwZE5zeHE5dmpv\nSW8zc2lQZWV0QUpxZnJvVFhXK0xFNE0KLS0tIFVxS0pFNFJDRFVIT01lS3ZCQm02\ndllQV3FtYjJHcmJuWVZSR3MzQmVTQzQKfA/QgdtihvvYWU0WBefz+w5PhPjR31BF\nzTcv4Ynd1tQRy4mDWcPVgcyJP4nN8GabfkBAA0+pjI2jcXIl0sWaRA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1swlyyk2rzvevqawyeekv75nx2dz34zpe3xqhkqme26gcgeavy4dqrfpcd8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1ak1objdQYVdUeWl6RWNW\nSTVsZlFnblIvRnJNVmRMdTJDeVd2NGlKZWpRCjZ6ZjJQQi81OHY4bTNqUCtiaWV2\nSUwwOEpRNEQ4MWY2cDdwS0Q1OFBUaEEKLS0tIFF4WVlUbDdFNncyNXR6V28xbmpj\nVEkveXIweEcwWi9BdW9rSEZuakN3djgKmiHdiJypk0r+EWWHgSmw7+Yd1f59m3Ul\n6rIcw9j7Cjy/9HUGkfqzZJIVf+TSBpCh4BOY6Wwnyi2h2CvKvaNQRw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-10-09T07:44:42Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:h/fejv7NsOKoVkb1Jz0LejcSof5j6NlIfpTjfirhnmaw3rO2aa1G5/ZRUsNNcEfxqcahzm7Y/ucs+nSoA8uyJa73pE98sctrUPLis2VfBHy8EY4UQq/3MS62VLDY9tCH8VSvA0+/IKtjCsFyeEMS5nzYwME6A2clvh6XQq2cpE4=,iv:oKI4v1SuH/9pUghbGiVP0+bG8adNmFB/groeuXfGe2E=,tag:YhTfSeZ/vSFFRI7h4+5S/g==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/users/kurogeek
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/groups/admins
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/machines/almach
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:C0rXbTr/YQ3db7PHAUNQC3lvn8MFlsMnkEMd342f4v9R6e5h0BomePsqhgd2UNjO7P1DWxTwDVhjWsCiVct0JgjwbAEbVTDcJHKEIPLs4+oDj5ZACZSUr4vp17ZkaaZOdIN3Ywy8bnV/pCr9F8xZ6caLCBvNSR+g40xjlpNyJdcUaGdQYo4tmUlnMT84cmpubF2lNUTHSLhYzunKvO49NCWlS1CNalcZjW39whhfTqPiTMp7J7r37yXgjLX2cHdL6mFatrs/Xxwj82hahb5a3QJvgXJ6+UZrurirrGX7Q8rfiGKNUd6IWyyVL+0To/kTPGzkQ4Kz+gW4WMugKO4vwqA+mwL3BbHdLhIakgFI,iv:pS3syLhM0N33YJPJ3MoQHspjV+717qSRgZYllq1Z0is=,tag:6CAaJ4aF5Dv5vQOSr2qpQQ==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1VDdJMXR4RTk4MVVSOTdt\nL1ozb09JRzdvckM3dTdCRFRMamZHNHpBT1FvCm5ucG10bi9rODdOaXBIMlhvV1NK\nVWpUdWgyTzNpRWx1UUZ2bHdjVTRGNDAKLS0tIHFrTVdOQVRDUTM2bk9RSndISlJy\nVWNoaFh0UmErOHFlemtjZ25ETXdyVm8KN6AdmVGo/HPUYmSdJkTrtuMPNluU9X2h\nEfXvT8rjxZTd4SwD3r4AktVIRhXq/0bwsPNTbW2ZpbdJ/CJ/pT5S/g==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17d4qt0n9edq57tgcqyk8eu5mrendl59yt6z2y3a4vkq7el8krqtq6lq28g"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEM2SHFKSUNx\nKzBMK0dmZ21iWlFwMWNxcFNzVitJRW50Z3E3OTUrUHJYUzAgQVEgd0l6SlBCVW1v\neDl5N0lHYmVrajZzVzVTYjd4WVdNVjZoZVlkUEtmVXlMdyBIdmtoczF0dmhxL1py\nS1hsc0ErckdoVVB0bE02RGdCNW5raERXT1ZJMTkzNjRGNHd2T3ZSTVYrKzZlbEll\nVTZKc2JVN1B2SzRuNkpaekhwSWJQaFZVQQp0OXRybUdwVHBxWHFWUWtodnZpQkJ5\nK25sNDAybTM2Sk5JOVJTY0NGb1hZCi0tLSBhY1lkTkR1aWVkUGM4bnhZRFIzRy9Q\naSs2SlBvYmVUK3ZZbURKdUtjTk1vCqIroKG03zORXYfC8ySosSUKJFLtOW3YvZxq\njvGOi7gdJ18bBcbq5DxWZMremfYoWeplWegYIt4agXmaGIq8nXE=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpf43tgcfjm048lsqskvq34w2t4uvrm5qy6m2eg6zjj82ctca8wctgpczxvj0q4y6337uhvsxdh5j86k9h9ymautpvv2759ucwnef75ez7pa7fpkddklp40mxk2tedsp74359g0kefn5rsq0x0yss6cu4yd0h06up0rp08t6yc4l0hfa9y8jn5fkx6nk0hjhz06ykwv0fyxe7z42q683jy0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIFNjaDhaZFBo\nTUQwb2NIbFo1eVZIcTFqWXN5L0VwZEcrVU02UXVjSHlYVncgQVEgZlFBRFNyWkNS\nRDJQaUJ3WWowTEcwaTk0Z0tiRHpQNmhkMTJZUlZHbHlEMCBtMzE5bzVQb1JmZkpy\nY0orbEs2RzBSVXA3UmZYZkRRanNnc2pHSnJjK1pLVmZOWGR4d0IwZjh1YXlkakRx\nTm5EMXR3ZjNHWGl0Ty8rcHpQbkcxRTZmZwpsTGh6NmljQzNtOXhjZm9Nb2VMTnhW\ncU5KOWNKNE10TmcwMVk4bjlwQzlvCi0tLSBsdVhIREtyZk5qdkg5a2Z3N0RURVR3\nbU9ualB3ems0RUNRMGM0c3dGcmN3Cnqneq/mJFWsJMLrZD2yUGd0gBqliC8OK8iV\nC1JKHR9iZ4vIrdPDhC3S0AoIQ/iIYNUs9F36NoXnWzFYHJnDtQE=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqprw0vfpc8wzsu78quc777kmee54ln6nnsjrnrhl7nr33eh4kvkksqp05qqxj4kgfzrmrugrsvg7skx6ghh3q9xc0x0agthtkvy25d9eq7eklta5wf7s30hexkuyl5546rdz9ffa5tawlp5yweqkgccntw0ny540n2am3cqw3luhxkfmrp63kwr6mwplhr9u26wll48x0n3k5f60c7hg9a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIGk3WlY0cFA2\nVy9nZzBpemVOSjAvV0FRVGhEaVJWK0NIdWt1UjYyY0dCaVUgQVEgZUh4S2o1R2RE\nV0svbnErTmxodXE2bUxJK3lla2lvajB3OXVwZHJtcHU5dyBKU3I4WFJnK3BOQXl0\nZDh5NTB5ZFlteFJIM2FVTVp5cmNyRXZqL1V0c0l6L0tSb0c5ZU9jbmRXMTIwb2c1\nQjZsREgzWUxaOXBUd2pQK2NncDZuUmZkZwp6alF6dVZzVHJRQ2VsNkoyYUY1RTUr\nMm9yZ1R4VWZCUUVMTEZpZXZTNXVvCi0tLSA4NW5mN1RFSXkxOXpMb0xzWUVxUlRF\nTkFJVElNM0lySWpOc2dOT2NNelpRCuQeukSZN4c9n90QpSyzZv1pA4yr9svAcdA3\nGAyBu4LV9Fd2cO9j3rN9iLF2qUR2toAhIGlJBnIyGHtVTiH+++o=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpyewum3q8dfcumfgec8nn958aec9f4q9aqy0k06kw5kq27d6fdqdgp0p7y4ru3n5xk90u747xevxa2af3v37e85j9g3axrmw5hdwdfh0wz22hut5vrafxsx26a7vh8fjwkymz3ramfgvvu4detztu075kmpr8l9ydqda0rnjwatdwmfgswg849p37astvld98s3nleeq575azlwc2hhpuh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBemdVa0hwTUxHSVFpN21r\ncFBJSzdtcHFOa3YwdC9PQzFkT1FvdnY2UHpvCkZybUZCWHgvMU5SUVlBOTA2ZnBw\nR1VVSU1aL05PR093TlY2TitWelF3em8KLS0tIDVTOFBoYitSallBVGlucTh1TXBC\nV3JnWUVXQkNzWEV2WCsreWhjeFRvOW8KKL+d+uJyGTmUaxvL6QlSbCPPLfcMgqEr\nufojmvBYhLsm7m6a1QUTxtdMuZKN5Wd4VdhIFE3JI5YaUzdTZY2EHA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVcUE5VFp1WTR5TTV3SEFr\nUnE1YVpVRjFMQzJsR0phNzF6MWFGeTh3aUZFCmZwOVNYSTN1aVRLQm9PWlhEWnFG\nSSs1TVVicnNWRURHZUU3Zjl6WG11MjAKLS0tIEpVWXRHaXVDYzlIOW1MSk9qajdr\nZlR5Y2pvaXNJTGdQeG5sZDFrU01odDAKEx1zKdU6x6uNbpyxLAGSNYxaaj+Pgj+7\nBYU4PSeN1PPqp7el13lfHscUHUG4i5OWRY4VBWF39DGgp4kSXawyJg==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1m2e4c3gv7mxyg839gf0lfsh8wvygx3rn8u5cwnn99vw2w43ssers40tlq8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0RTZyOENUUmI1UUJEdVlQ\nS0xEMnd4a1dHZ00zcUp1U3VzK0xvdGJsVEJ3CnFTMXF3Wkd2cWdPVDYxNGdZUVph\nVVRJaitjUzY3VUo0ekw5dG5kL2hRaDAKLS0tIHpWOVRhZy92ZXpTWFlnTGNiSUti\nL3d6dE9TeW9reVBJMnNwMnNrelZxSXcKOPfEEv1c2k6bHZJ+i5v7XKZJxhfkJgg3\nsfgiG0jKKuzERCZTBXNUFzEWH3zw96LAcrLT6iAdZSfrDOYQw5O8yw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sg0rvgyetdcqw7j2x983fh69kdkvqsngpe5x36e5920qa7fze3cqhj4wgx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAySlRBdTRobjBKVTBiTmhD\nZXM5ais0ZUE4QjlQTlA1RU5ObUtna1owS1hjCksrNW9LMzJKUkptUDAxZ1h6ZGFi\naysrelFHZ3pCbUd2d0xhZlZOV3V3V0EKLS0tIE1LVXdMTHlpcWxaTm9WeW9EcEFu\nbi93TVVzdll1aThzbDFPd3dqNnlOZEkKmnmiOvMvdxrxrxORf5Xqu9zZ0zLQIiSI\nLPI2oLaka0XHHe9ntHoINAT7MgZMSO6whELRYlIOjSY4BOTPYUN4eg==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1swlyyk2rzvevqawyeekv75nx2dz34zpe3xqhkqme26gcgeavy4dqrfpcd8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBINkxtem5NTTV2QUoyYUxZ\nTTNYK0JQS3pQbzcySkFGRmtyMXlnVFRwaDJvCk5PK1VRQ05WTDBUakxYNUtia3Bv\nUUgvSW1PZVhyZWhXVEcvWnZqMkZUOE0KLS0tIFRpNkpzQXJOTit4enB6d2FIc051\nL216WWNESU45U1Ziam1TZVUrUGJ2N2MKpuZHpZHGQZzQlsdFiuw5siIReOGe2zdw\n2uMl25AvnWsWtoydEYzDPSbz9AIifUZy3TpzNZ6MzlL670pfANlEiQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-10-09T07:44:42Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:EdR/UIwCVTxWBUAAzTBWVPqjA4wZG2YYV5WtsGzvfC+X3noypuKKkqGRhOwYeTvurc9riNByQbXwob5TqLcx8FmnGaaEY3/1SuOom5eJo4kIDdd41ETyqsGyHNwCpY8EpafmnTQjed35t8B9xyQUtasF8m6O7vj7shK/HBKAfwU=,iv:N5/YgfoMLDJyYzxFse842I5mITThzu884yOIZdDFPOc=,tag:IVjldPzHM1nLoMk1wxYiIg==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/users/kurogeek
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/groups/admins
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/machines/alpheratz
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:1/BI/yaoHAnKCvXeUMtuFi074H8iKqbhRF0nW4Bxw7mqruHGghvBOWdh8oS4EeC5voruKmJHXUpjunuQIPC4yZn8FVZ6y0SA+No1R2zAGSfIcmEeB9MbN0Bq1xIbK3Meii8ocYfEKtZVTcp5MiRTbGxXWZJkQHQkCDEoZ2FQrJkqRmLpHogZpuB7De/8auj/HniRPSwvNO+qPxUqjdmuVyTc8h41Wsjqbfj3nQKZaMKLHVfOYVStYyTFhxcvAAY4+k2I9rCJM1gpvwJ3ASwYguOWNYGoMO0QAY7HlvAqt/34mw9HoAuj/9dKX+wRvjEcHFPGMhWilUreXc/nFkNhamuQj3jBzFwlOZ5gUrc+,iv:hSPk3gZMEzhO/TTwnh+JJsDLrCuo0ezF1Ou92nByuX0=,tag:6GmCP2Rj6g8Q+1/jXatlGw==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBldkRFcStkREx0OExjWFYy\ndEIzdnRQNnZjdWRHQlVmOTlsVXdnMjl2VVVFClVTSmtocWk0ZjJZWmZkTzBKb1lI\nVnRYeXV2VTI0OFdFZEgxT1BITkVVbVUKLS0tIENZVzVoejVFckkySzFBdVRHQm1u\nWnJnV0VOTWc4dWNKakNwT1NPdnp2b0kKQELeL2FOqprZ0ioGIEXet/v7/xHL5+LJ\nFZu3TNAfTgq4GfnZ81IJpm+0W6emiYQIojtmZjI1J1sgNQk9rlR10w==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17c9908f08w2w0ajprep4p60a7hd27cznrnxmq4mxwznvgzjuhymsxttd3d"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNT21ibHg1alUyZnVseGJK\nMTRaS2o3b2FwUjhQRHgvL0thTUluV0xzM1NFCjhvTTBOcVZDZ2tMbExTZ0ZYSnhj\nMUZRQ2lWbjZJNHhkcjYrZ1dsUjNTbEEKLS0tIHFBMFFqM2h6Mm9zQitTZk50bDh0\nZzFPUS9jMzArWjV0T3Rja1hYeHJ5SUEK9fWE8AfIiLOktOz/T5iXsgOMbIdzDavP\n3ecNfrWt5Pkwq5U1WZJgB4m8OCxOxmlalcpSoyuQnhnaeKwDtrpcNQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17d4qt0n9edq57tgcqyk8eu5mrendl59yt6z2y3a4vkq7el8krqtq6lq28g"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEdlc0lRQ1hV\ncjRyQXRZdnhnWlU1SFNQYmdoUWlwaWIxZ0FSdTI3YjZMaG8gQVEgd0l6SlBCVW1v\neDl5N0lHYmVrajZzVzVTYjd4WVdNVjZoZVlkUEtmVXlMdyBIdmtoczF0dmhxL1py\nS1hsc0ErckdoVVB0bE02RGdCNW5raERXT1ZJMTkzNjRGNHd2T3ZSTVYrKzZlbEll\nVTZKc2JVN1B2SzRuNkpaekhwSWJQaFZVQQpVb0gyS0ptS2JmVDlpZ3VPUlNLYkNX\nWkZrR2VydnNYUjVPcnBCTmtVRFZJCi0tLSBQcmsrMHBMZXdvWUg2R1NvbnVCbHFq\ndG5MMTlnTG1wMW1PdE8rWXdKeTVjCnRsPfPMojLJ69iYUls41EMcpyj8/+GF0Jhl\nsooxmxvuwGhOPXuWTewFsEji1yfye6rsgNLA9Cd0RCpvyIJw0P0=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpf43tgcfjm048lsqskvq34w2t4uvrm5qy6m2eg6zjj82ctca8wctgpczxvj0q4y6337uhvsxdh5j86k9h9ymautpvv2759ucwnef75ez7pa7fpkddklp40mxk2tedsp74359g0kefn5rsq0x0yss6cu4yd0h06up0rp08t6yc4l0hfa9y8jn5fkx6nk0hjhz06ykwv0fyxe7z42q683jy0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEVUU1lSaytR\nR3EzbUdrSG5LMndoTjFYNnlIVVgvNU5na3JSekxOQllRSHcgQVEgZlFBRFNyWkNS\nRDJQaUJ3WWowTEcwaTk0Z0tiRHpQNmhkMTJZUlZHbHlEMCBtMzE5bzVQb1JmZkpy\nY0orbEs2RzBSVXA3UmZYZkRRanNnc2pHSnJjK1pLVmZOWGR4d0IwZjh1YXlkakRx\nTm5EMXR3ZjNHWGl0Ty8rcHpQbkcxRTZmZwpxT1lnNGZyZFg2QUxWM000VEoxUEVy\nNjBMcFFoeHVaQThKc3IzRVhuV3pnCi0tLSByckIxTUZVQWtnalByTHBuaXN1NDN6\nZG1PcUhNUTYzL1JBbW9KOVVRZEo0CgQg8XcyqPKDCVKwl0Dl2etfAv92GdKDRYM+\nAWO6BLNlf77kNpdOlUPW4UHkbm/RLm44db6nbJnxhORn5ywql1Y=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqprw0vfpc8wzsu78quc777kmee54ln6nnsjrnrhl7nr33eh4kvkksqp05qqxj4kgfzrmrugrsvg7skx6ghh3q9xc0x0agthtkvy25d9eq7eklta5wf7s30hexkuyl5546rdz9ffa5tawlp5yweqkgccntw0ny540n2am3cqw3luhxkfmrp63kwr6mwplhr9u26wll48x0n3k5f60c7hg9a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIGZLTnFMUnJC\nZEFsdVp0aTNxSVJ2OEE0U202ajNzTGFSUTN3bkZTMlZHeGcgQVEgZUh4S2o1R2RE\nV0svbnErTmxodXE2bUxJK3lla2lvajB3OXVwZHJtcHU5dyBKU3I4WFJnK3BOQXl0\nZDh5NTB5ZFlteFJIM2FVTVp5cmNyRXZqL1V0c0l6L0tSb0c5ZU9jbmRXMTIwb2c1\nQjZsREgzWUxaOXBUd2pQK2NncDZuUmZkZwpLZDhXb3pJdVRUd3oxSnJ6dWF0WnJH\nRS9aN1pvZllLM1oyM3pRanczcWF3Ci0tLSA2aEhIQTgyS0N6T1pmNFo0M081bGJu\nUEkraTh4YVBnOE1SN3BacUR6RTVBCunmdr+N4+GF4pggFEnR+8Pjo8sid/O0j+EH\nwA/jT3rreM3G/IIZhiXPtlg9IRQAsops1AWpmRtXpg+m3ra5O7I=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpyewum3q8dfcumfgec8nn958aec9f4q9aqy0k06kw5kq27d6fdqdgp0p7y4ru3n5xk90u747xevxa2af3v37e85j9g3axrmw5hdwdfh0wz22hut5vrafxsx26a7vh8fjwkymz3ramfgvvu4detztu075kmpr8l9ydqda0rnjwatdwmfgswg849p37astvld98s3nleeq575azlwc2hhpuh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYN3hvS2FTS1FKaUVLdjk2\nN2E0RDBoZGx2djVTYkxOU2JQNmoyaE9XeHdBCkJmc1pzSU9iOXFTZW53ZFNSSktS\nUDFaQlJjZVhIOEh3TkxEYk9GVU5peHMKLS0tIDd3N2lia1B5WnZJQUhpTytDcENK\nQlQ5eW1rLzFKZndWVmZaYmJqdnRhQzgKqNW6DT439xoXARintD8N+baALXqH+Fg/\nmBw2wxY8vXsdhVHnl1JQ7c5xKhsXZrPnuOokoSq1drjgoZaTIrvo8A==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5UFhpWmRSTXkxM0RnL2t4\nMWRkVWhQSFFIRU1IK3BRMmpLYVhic1M0Nm5vClZJb2dicGFnVFFZdGlnQlZkNUtp\nMlRLcEk3eHJNZVZOU0ZPbldpbGNxRG8KLS0tIEVCSXA3dldodEl3Z2hHUTg0OEEr\nSEtsMTVxT0t2VG16S0JZcWNSZGZPYlkKGHVe915N+bgdXGjrLiOeTCGFv/tHPdUZ\n2hRQW0aPFwAq9S2ydJ12h47LfhhBPYpntDG2JRaoNHlcx87na39Fnw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sg0rvgyetdcqw7j2x983fh69kdkvqsngpe5x36e5920qa7fze3cqhj4wgx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOL2VWNWV6Tm56eTNIZk1M\nTlVMdWtjb2R4cGM0eHo3S1FLeDFqdFNEYlc4CnY1TmUrSENGdGtCUzJ0OW5oOWxq\nQWU5alZKNC9OOVNZeUJ6N2xnK3RXc28KLS0tIEpaRVQ2Wm1hV0dxUFRFVS82akNy\nL2ZIUm9pTWpaaEU1QlMvMEZMY1pVVlkKhT2ETLPMpHqe3tflE4H9jxH4fesR1TgF\ndHk4i0bCkWRJZCTDYKPmGMkHRvanUIf7u0lP6rm/4qdA36g50fTleQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1swlyyk2rzvevqawyeekv75nx2dz34zpe3xqhkqme26gcgeavy4dqrfpcd8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjWkZOdmQ4T29zQVZnZUlZ\nbklXN0dvZ1BRcTA2dXFINEhIbmZJdU5TWjFjCndXWHFyL2J0Tmt1YnJIdTVCWitF\nZFZ2QUgvWUUxV1UwWXJsQURnbkQ3TkEKLS0tIE5YQ2tzOVVjVDZzRnl0dCt2bGtF\nUXdaajNuUnBxS2pQUzJEbWgxOGlNZ3cKCau1pvPuaIDDDnWvwGTFWU8ozLlCHfFN\ngp2lGxlGU8jI4yjfTT83+W15QXwbQsSWssr3ex7zkRKg0hKabQHzvA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-10-09T07:44:43Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:5gW6xx0tNOh9i6v6+etYYCfWmQ7ISW1BppRUX58Mq+Ulhz0wfYyRTiiHZqiDzUYUl0lCk20VLaGPynAIwIda672bmK5OvGJC/9HuBuJoKRpSpRgJ4uF3pKS4C5DBMKkSXEl3efzCZQ2xrwISNBFQnXPzWBX4Fo5nYZOtMTTwRbw=,iv:/H54jQ7cto6RfAxNYs4CXjcdZ2Oguzn64NhDY+8dRjE=,tag:+p4fQf3cqdoyqDJnpw9vTw==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/users/kurogeek
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/groups/admins
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/machines/buna
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:QaeDS07zlZznnp51cjGu8RhinPA0QEQcKv6qcfLh5xtivdttkScAp9tX4D8eepXQ97YsB1JJdPpp6gq+iW8HXyem+tYrkuoVvWKUzydAhvepyRi7cgyBV9YqI5OY3LnMSIpaDrcibKvV8qyQCD92HXeDS35ufE+0AOhy0TeO7AEZ2uXEc3BoJRvQW9lrApWMrfPe+6QltizCUX4xZk8DHdmNu+KsOogiUO8KGx5DCjyGYOugAgMGHbd/ZtkwOWgyGMzSMMRlrOdumpGJacIVH+Clb1EOtpK1URzJy1RgeqqP8cKEvRailR5VXyZviEpUGecUfu1T7LicBkVn9b8+EGHM3lH4A+lahRJm8JYt,iv:Lw1B9JrVC0bs/ZqILYjJlSk6zIyI3VyPvTWSDV9SDmo=,tag:dJnaYhLRdQE4UJ9tbItjGw==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6M3JhMDMxT0FLcVMyN2l3\neU9PTFM2ZnJISHJ6T1NLM0d4a1NRT3NWNHowCkRhZlBmS2xRWnNxSFlEb1BmR3NV\nN0FmMWtsVWp3NzhvQmZSZWYyb1FXTGMKLS0tIENzaGt1VjJKOTN3TFExYlhrK2hn\nUVhGZW96SDFzK3FkaFdVNTArQXVLK1kK/eijw2CfYeUDP0mN6gK3cVlasrKMDf9Q\npJAE7j7qJBY12+KCaH0U7GeU4qe0rQW5gQvZAp/Rjau3OAyBmJtuPw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17d4qt0n9edq57tgcqyk8eu5mrendl59yt6z2y3a4vkq7el8krqtq6lq28g"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEF5UndoV1hm\nUVNNeHFuWjZHRVNzaGlGeXpyalN5KzZURDNvcnRyZHgxU0kgQVEgd0l6SlBCVW1v\neDl5N0lHYmVrajZzVzVTYjd4WVdNVjZoZVlkUEtmVXlMdyBIdmtoczF0dmhxL1py\nS1hsc0ErckdoVVB0bE02RGdCNW5raERXT1ZJMTkzNjRGNHd2T3ZSTVYrKzZlbEll\nVTZKc2JVN1B2SzRuNkpaekhwSWJQaFZVQQpZWmwzME1lNUNzelFGU1FUNFZzSldP\nRmtuU1l0a0x1MUFXbzU3MmVkU2pFCi0tLSBRY1BUQ25qa2ZqcXhYREQ0L1A5VUV3\naWhDaGpNMFU2eUdWSmpJT3dNbWQwCsdznbkReqYz+oeevciXSl8aAJd+dMXIGJMj\nyJj8yIwIo7S/EP1eamg46UYwJdEdZzvKXHCNp+NwHGMUSNMiwew=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpf43tgcfjm048lsqskvq34w2t4uvrm5qy6m2eg6zjj82ctca8wctgpczxvj0q4y6337uhvsxdh5j86k9h9ymautpvv2759ucwnef75ez7pa7fpkddklp40mxk2tedsp74359g0kefn5rsq0x0yss6cu4yd0h06up0rp08t6yc4l0hfa9y8jn5fkx6nk0hjhz06ykwv0fyxe7z42q683jy0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEwvcEp2Z0pP\nL0VHekxtY2NIcVA3ZnFRc3hxTHhvVGQ3WHBzVDRRd0RLa2sgQVEgZlFBRFNyWkNS\nRDJQaUJ3WWowTEcwaTk0Z0tiRHpQNmhkMTJZUlZHbHlEMCBtMzE5bzVQb1JmZkpy\nY0orbEs2RzBSVXA3UmZYZkRRanNnc2pHSnJjK1pLVmZOWGR4d0IwZjh1YXlkakRx\nTm5EMXR3ZjNHWGl0Ty8rcHpQbkcxRTZmZwpyZHdkSTcvNVd6MVQ4eGFsdEw0cGN6\nenNRNHptZU9xN2k2R0I0MmZLdXpJCi0tLSB2clVhcTAzQU1RQ2lBUHhzWUVLRDJn\nY0N5Q2ROd0dVWHVmcjE0YjZUWG5rCgLBnzsYiVYjAOVy3i+s+rJlBFAWQWAgn0/6\nNm+u4QgqGvDSc3USB81KyMIUDbiQB7ppcEooNc39TvsV5fECrX8=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqprw0vfpc8wzsu78quc777kmee54ln6nnsjrnrhl7nr33eh4kvkksqp05qqxj4kgfzrmrugrsvg7skx6ghh3q9xc0x0agthtkvy25d9eq7eklta5wf7s30hexkuyl5546rdz9ffa5tawlp5yweqkgccntw0ny540n2am3cqw3luhxkfmrp63kwr6mwplhr9u26wll48x0n3k5f60c7hg9a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIFRmMG8wSkFa\neFd4VG5lYVFxcW9vditscU5SN1Z1QlU4SVlrU0k1VlB2MkEgQVEgZUh4S2o1R2RE\nV0svbnErTmxodXE2bUxJK3lla2lvajB3OXVwZHJtcHU5dyBKU3I4WFJnK3BOQXl0\nZDh5NTB5ZFlteFJIM2FVTVp5cmNyRXZqL1V0c0l6L0tSb0c5ZU9jbmRXMTIwb2c1\nQjZsREgzWUxaOXBUd2pQK2NncDZuUmZkZwp3TVJ0UXFwbnFNQXdFNDd4LzZDcUN0\nRzBmZUE1QllZVmJ2Q0Z3bWNqdGdvCi0tLSB5UG4zUVVuNXkxUkkxWWVmVCtWYTBQ\nazhUQnAwK0lBMWlhRXB5N1dPNEpzCgH+Dmfy9/Yd3DnaRHIA0pFo8wcZgGdQd8N0\n+pz2o1GnVSoC5kq0NxY+sFKEvVbL6wl7V6kIJ3fGclsSOQaXFvw=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpyewum3q8dfcumfgec8nn958aec9f4q9aqy0k06kw5kq27d6fdqdgp0p7y4ru3n5xk90u747xevxa2af3v37e85j9g3axrmw5hdwdfh0wz22hut5vrafxsx26a7vh8fjwkymz3ramfgvvu4detztu075kmpr8l9ydqda0rnjwatdwmfgswg849p37astvld98s3nleeq575azlwc2hhpuh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxcG94d2NnVjV1eVY4UFFw\nTmhFZk05cDdvdDhOUnNtK01lZFR0c05Kb21vCjhMT09WR1EwZjRDOXdTaWN6VjJo\nelQzbzBuRzJyYVVKTDE4WVcwcGE0UzQKLS0tIHR3SW85aXFhd29RV0dvSUhtMkZM\nMTF5VjFIRVRKQUY5RmhyTklvOW1rZEUK62GxTlYLJOOjlYJl02uygjSt2yewjoMX\ntY4mw9av6gDRbnVDNkP3lLhDQA5tGE0KpTvcGcTOjkdzPIBb5pygqg==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnTFU5MFhQMkd0UGhtWEIz\nMEZscktHSTN6aFJVR3grWjRMVk5qMGlaV1JnCm8zRDNLVGdrZExDQXVxODFJQ2pM\neFMwMUZJeWpkTllKL1JXTk9WQjJhSXcKLS0tIDhWUjZEMnhWblM5eTBsTjZVSFMw\nUFh4YmdOZWZmcTAwdEFUVnpqeUlvWDgKpyC3/jxXEiV5A5U1SLkgXuWkRZFFt2fe\nvKhmhBXqyD2PbqAvdmIRBbh3L89oqsgEFr2md9Kvo9piKSCtWVcwMA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1pgxgvwck2vr3va6c4c3utj7w2rfg2st9qvm76nc92vhfmplqmu4qs928lm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUaWFEdGd1UmJ2R000dzJI\neUNwSVVYM2JYc0ZleUozS05uM2EvWnpqTGlRClBSQ2VQR3QrektXTWo4YjBuZDJj\nUW44UjNEeSthbWlxblUrY3VjYnVRdW8KLS0tIGZNdnVwRFdnbk1WcTcwSVV2VkZV\nMnRIVmd6ZzhQL2JYemgxTjd6RGFSY0EKGOT6w/7Kbn42afcyU9vWkIWW9vCHDzOi\nft+z61VK2z0P/VCq21dcBVW6q78CEg6v3eJEbhAdMQZvK11hLf1zYw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sg0rvgyetdcqw7j2x983fh69kdkvqsngpe5x36e5920qa7fze3cqhj4wgx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwVzBkbEN1Uno4UE9uSXBF\nNHhMc2ZxbHBnSWNsMkRBQ1F6c3hYOVJOblNJCmFrNnkvRnlFdm0ra1NBejVtS21G\naVl0RzFKL1REanZJUWh0VmtTQjFGNWsKLS0tIFNDekloQ0ZqdHBMUEllZkNCTG1r\nY3MybWQzdjNVQXhuUFp3Q2dqWDIrZEkKopZMeSmWY1JAbQMw92F9UsLr+WP4k/Sf\naFFPr6p5MCfvISFXfi75XZK073ylg9jISSBIoMOr+49Q+QZH9qeP6w==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1swlyyk2rzvevqawyeekv75nx2dz34zpe3xqhkqme26gcgeavy4dqrfpcd8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwajBZTWI2Q2o0a2lkYTVt\nWHpSZzM0NExRUVlRUVpaR3pMdFhnaUdjM0g4CnFXck1zWmxYSzdOYVFPWUZLc1J0\nMmttU1kreklaQXBZMllWSk5TR2ZVdzgKLS0tIGVMTytxWmJ0WUFOc0xDU3puRTY0\nTlNXakxKenZJa0NUZ0hueXlKOGJIY1EKm0OCBDKnM4gSB6sh5DU98EXv2J+LxXyR\n4V213yMXE0QuTkXCJYeaTKyNT0BhlJ51TF8TUXfpiP7+W/py1ADMdA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-10-09T07:44:44Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:gykkTbBTzcHSl/p67q38V29VJjmlpWLzpcn2/wAtFf3n8S7m+Ma1xCGUbsVnd4mV2OMClXKjdaz5wN8rNhkfvQy8jnWDgdmTJN4Z3LawvmXhuzEOmViv4uIuTLqTnmfrc4BNGTgajvtwGIz9vDVmBvMFv15b7xFUY3wU5xuAdsQ=,iv:XACWsdOFU6rRPfu2eCsKI9Lbt1cqdgPEQLn1vKztOHo=,tag:eBM4wtrmZRiXx7/qgATA4g==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/users/kurogeek
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/groups/admins
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/machines/mirach
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
{
|
|
||||||
"data": "ENC[AES256_GCM,data:ICq7CQHrcO4kuOQ24kjl036JWx6dnS5YVNqU+eJeHi7QZ0WtI4XTSzVsdZ0slCGTwIrDY4knrPFvC6zoqtXVimQCE7qkvrr9GgNS4JdJgMmq+9tuA3f9nvf2Z7JDfGWU3Ht/gQvHlmu/tN9Wtg/buqpdk93lRXkBi95Fe6799ocFmus3vKfkFIddcq7V1wkfX774OaHLE3/ho+dMuQxUcgSuy29rQwHr7l+DXdJQ5gUXk/z/XAU30SVFOnSH92DjSDMLnrg9hkgPHgaFVNAgP3hG+C6S9WFBNuz2Z14jm/0ylTKjANyqLrX9wRi4SHFc8IDyFW4q8xqZy+Fm1LkYTehG4Re10F9wgh3q8vt4,iv:Bs9WJNERCDOQktKhdxZzAW1k/bzdvUTtyM1NU6/tThc=,tag:CD+oBGHCj+lNdEh9elL9sg==,type:str]",
|
|
||||||
"sops": {
|
|
||||||
"age": [
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMdDBSNTZSQTVPVVlGNXI5\ncFpCVWJhSHIwczJKTUdZQTJQTE5RVE5XUERVCjFpdDdJeEVRM3k2YlNPaEk5dzBK\nNldvUUF3eE9kaEg2c0ZvNjl4Lys1dFkKLS0tIFpBam0xRjFkRnZjWEtWUHJZUmth\naEgxME0rUE5Ya25JZC9TdmEzRG9Tb2MKpLndT2smBLsxeKbCy310XMEM94NDUJI/\nLK9pn0IvPKndGETgUuOoPZNOvKNptEmk6DtmiwJx6M2gpNsQRogCQA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age15k6cz0lj207608gdlgxq6l2n5zsflvg0lw8qqlvkyk0xddc4kqvq9nh6ex"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBadjhaazA2TWJvVFVyUC9D\nSWVRRWFKbU5LRUpqeTBIWk80cFRQU1MvaUMwCmNPR0thUFRiR0R4OCtWRC9vbFdU\ncDZ3SU5iWC9BL2U5bEUvZFRnSWFzUVUKLS0tIEtha3Y5Z1VnRVVpS2EwbGxnNU11\nclhDTkdseWJXenFCaWhFejkvZ1QyaVUK4l+jug4AU3WKCasru29KrqOryUOoxXpL\naoigeE0yTDacBMdh8bMZK08HBzDgBW3q1WLi34xf0rR8FOk08JN3aw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age17d4qt0n9edq57tgcqyk8eu5mrendl59yt6z2y3a4vkq7el8krqtq6lq28g"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIGxsYTZOWVBz\nRnNMUDF1cDNwL0M0MTRuNlFNR01KaXJEN1FVTDNCeEM1MEUgQVEgd0l6SlBCVW1v\neDl5N0lHYmVrajZzVzVTYjd4WVdNVjZoZVlkUEtmVXlMdyBIdmtoczF0dmhxL1py\nS1hsc0ErckdoVVB0bE02RGdCNW5raERXT1ZJMTkzNjRGNHd2T3ZSTVYrKzZlbEll\nVTZKc2JVN1B2SzRuNkpaekhwSWJQaFZVQQpQSzZicUQ1ZDJ6a0p4YzY4blBtbHRv\naHVBU3R4S0I2dVRhZnFyR0Q1azZnCi0tLSBnQno3V3lQcEJJVjVpRlBGbjNTRzM4\nQ1B5bVc1d0FVYVNyK2tjbDMrWEI0Cg+VtL8ZC39HU76T8ZyQH5k9AN2ouj31P0Id\nhmEfWMGp6ilC0Fm+fMkUoleSO4Us1bc7C+CjwJN88xucNEkcKqE=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpf43tgcfjm048lsqskvq34w2t4uvrm5qy6m2eg6zjj82ctca8wctgpczxvj0q4y6337uhvsxdh5j86k9h9ymautpvv2759ucwnef75ez7pa7fpkddklp40mxk2tedsp74359g0kefn5rsq0x0yss6cu4yd0h06up0rp08t6yc4l0hfa9y8jn5fkx6nk0hjhz06ykwv0fyxe7z42q683jy0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIFpZTjJmWnN2\nVnI3UHArVXhkeWx0UkdLM1h4ZkdSdHdyRU8wZmVYNlE3M2MgQVEgZlFBRFNyWkNS\nRDJQaUJ3WWowTEcwaTk0Z0tiRHpQNmhkMTJZUlZHbHlEMCBtMzE5bzVQb1JmZkpy\nY0orbEs2RzBSVXA3UmZYZkRRanNnc2pHSnJjK1pLVmZOWGR4d0IwZjh1YXlkakRx\nTm5EMXR3ZjNHWGl0Ty8rcHpQbkcxRTZmZworcmgrbnM2bXVzdmRETzV2Q2ZxT1l3\ndmF4TkJIdjNMS0FDY3lJZEJ3a1NRCi0tLSBpWXJOQjhGUW5jRzhrdzQ0eEVNcTJX\nVGJzVXNxYUg2ZWdjbjRRWm5ibUNJCvzc5NMCGROVcGH6uLBL1ArMuSku0J/8cy4e\nXrriQktXy2Exw14jBC7w6kirE8zqRm8rQK5HNHDCudSNwSG1lM4=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqprw0vfpc8wzsu78quc777kmee54ln6nnsjrnrhl7nr33eh4kvkksqp05qqxj4kgfzrmrugrsvg7skx6ghh3q9xc0x0agthtkvy25d9eq7eklta5wf7s30hexkuyl5546rdz9ffa5tawlp5yweqkgccntw0ny540n2am3cqw3luhxkfmrp63kwr6mwplhr9u26wll48x0n3k5f60c7hg9a3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IGZpZG8yLWhtYWMgQUFJIEZuRWpNaW1X\nRXYxUlVVY255bUtLUmdwclR0SENGZnFGbDAzWEp5anJCRTAgQVEgZUh4S2o1R2RE\nV0svbnErTmxodXE2bUxJK3lla2lvajB3OXVwZHJtcHU5dyBKU3I4WFJnK3BOQXl0\nZDh5NTB5ZFlteFJIM2FVTVp5cmNyRXZqL1V0c0l6L0tSb0c5ZU9jbmRXMTIwb2c1\nQjZsREgzWUxaOXBUd2pQK2NncDZuUmZkZwpqMXFNT2tRa2cxT1ZOUU1EVXFzb2ta\nM0NOS3lRTVdGY2kvL0VzTE1Oa2lzCi0tLSBXQ1d5L2dZV0RtRDd0b1pGdXYrZklY\ncmFPa3owZ05zZEo2UjZ0YlNZc0ZRCiQKbOn4DMdVy25AjJ7J+bqLmt3cPTeErtYu\nhSUSZ9TW50bFEopZHLIMHmqEnfp4ckInGva9BD3rUmdwdX5OpEg=\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1fido2-hmac1qqpyewum3q8dfcumfgec8nn958aec9f4q9aqy0k06kw5kq27d6fdqdgp0p7y4ru3n5xk90u747xevxa2af3v37e85j9g3axrmw5hdwdfh0wz22hut5vrafxsx26a7vh8fjwkymz3ramfgvvu4detztu075kmpr8l9ydqda0rnjwatdwmfgswg849p37astvld98s3nleeq575azlwc2hhpuh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBud282MTN6UVpyM3V6OUE0\nV3cxWUU1UThibU83MFpiWGFiVWsxMmYvbkM4CmtoNFBWbWNGaVNkVnJEbUp1elZR\nTk9GL2NWVEw1OUIyaUR4WUh5aUh0YVEKLS0tIFF5b0I3UUkxNDN1c3NETzVPNjhz\nZlFnYm1URmE2U0NKUnJIcmwvWlk1cDgKyFm8sfGceuW3c1LQHJazv15Ghyz+af75\n3XxENPd8xXoRIa7i34xIFgXvU7hxqX517NtIXgJwHHboWfUueXPQUA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1hlzrpqqgndcthq5m5yj9egfgyet2fzrxwa6ynjzwx2r22uy6m3hqr3rd06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAySUxYUnY5MDdsMzZwNFVv\nYWhxSU16S2hLcHpPM2dWWTB2ZnM0OXhLMjA0ClFnR1hyNWduM0hRWFhxVld4NWVS\nbnlDMlRPbVNBeUlFbUJka0JkMjQ3NVkKLS0tIE5kZDZGUWFGQXJoc0JRYUgwRjBx\nc2w1RWNGQXc1Smw5TnVGZXlBblcvZXcKDoGAcdVZ8andZq5pNGGfDQfyQ9LW2XOJ\n2mJhLWiYWKei6xEG7eJ+Yl+De4R+EtNN+alp2gJLCjsoiTVd87PVqA==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1sg0rvgyetdcqw7j2x983fh69kdkvqsngpe5x36e5920qa7fze3cqhj4wgx"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFdGRpMVp3N3E5K25VS2Zm\nbTFpQWtMcUN4bytIdDNRT3B0RTNDMzNlRlJNCitvek4vVmlmWFlOc1k4eE91QmJ0\nT3NIMmhyWkh5UGtaTmVqQjBibERqR2cKLS0tIHpwNFlGVzNvQkRxUnAwNlM5NS9B\nb2laYkhrTlBVQ3hOK3RaT0tJaFE4cEUKx3rB2z3XVbXv9PkQtAA7I5QneNi8Jjrp\nBApe2+PFEPKjLq75QYCYZRH6ah08CYx77dD0O561bNL6J6oZCD1YRw==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1swlyyk2rzvevqawyeekv75nx2dz34zpe3xqhkqme26gcgeavy4dqrfpcd8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBVGV0M09LU3ZZRkV5cVFz\nbTlxbC9hTlNKemJ0LzBDc1U2bnI4Smp1U2tnCmNORzd2SWQyZFFkS1ZDRlJHa2d4\nWGxIaHBlbEZMZ2R0NWdrbHhWMzZHUmMKLS0tIFJ5dnNOUG96TFZybDF1TGdoQ2dC\nbFo1WTVYNlVVeFo4ZndXSytncy95SkkKSGv8EJsVnDCzFpsnzB2bkMds8bW82OUw\nz6uBFRR7T9JbfSHKLYqQRJVSNlu3jleKK+wak+HfGaUPPU4n2K0Qwg==\n-----END AGE ENCRYPTED FILE-----\n",
|
|
||||||
"recipient": "age1vphy2sr6uw4ptsua3gh9khrm2cqyt65t46tusmt44z98qa7q6ymq6prrdl"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"lastmodified": "2026-10-09T07:44:44Z",
|
|
||||||
"mac": "ENC[AES256_GCM,data:eK0kYfRDJVVKQ1FK2nTfUl7/Pm3dRRwjvhih1LMncBw6xlF8xLy+/CckucqDFgf+5PSl8Mti1G0eKo4/WoISEtDjYmmuSHM++KsVpKtmk6g7ooco8maUYyh8t7siqYzhBvqVt3ftlrA4kJ5f+BQm41QTGDqk33E7JaNglGRgh+U=,iv:BzwqnsQL6vBVzbJel2TOUxTo7QiLq2ujBskrQ1LIO5I=,tag:pWwtZlcht5eSTjz/CrtM3w==,type:str]",
|
|
||||||
"version": "3.13.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../sops/users/kurogeek
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
fd6b:1ec3:3bf0:8d8e:ce99:936f:daf0:109b
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
fd6b:1ec3:3bf0:8d8e:ce99:9313:20d:e30e
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
fd6b:1ec3:3bf0:8d8e:ce99:936a:93d7:d67e
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
fd6b:1ec3:3bf0:8d8e:ce99:9375:cdf4:e6de
|
|
||||||
Reference in new issue
Block a user