claude'd better security things
This commit is contained in:
parent
9e35448f04
commit
f9515dd160
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1 +1,3 @@
|
||||
secrets/** filter=git-crypt diff=git-crypt
|
||||
usb-secrets/usb-secrets/usb-secrets-key filter=git-crypt diff=git-crypt
|
||||
|
||||
|
||||
64
age-secrets.nix
Normal file
64
age-secrets.nix
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Configure all agenix secrets
|
||||
age.secrets = {
|
||||
# ZFS encryption key
|
||||
zfs-key = {
|
||||
file = ./secrets/zfs-key.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
# Secureboot keys archive
|
||||
secureboot-tar = {
|
||||
file = ./secrets/secureboot.tar.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
# System passwords
|
||||
hashedPass = {
|
||||
file = ./secrets/hashedPass.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
# Service authentication
|
||||
caddy_auth = {
|
||||
file = ./secrets/caddy_auth.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
jellyfin-api-key = {
|
||||
file = ./secrets/jellyfin-api-key.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
slskd_env = {
|
||||
file = ./secrets/slskd_env.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
|
||||
# Network configuration
|
||||
wg0-conf = {
|
||||
file = ./secrets/wg0.conf.age;
|
||||
mode = "0400";
|
||||
owner = "root";
|
||||
group = "root";
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -14,6 +14,8 @@
|
||||
./hardware.nix
|
||||
./zfs.nix
|
||||
./impermanence.nix
|
||||
./usb-secrets.nix
|
||||
./age-secrets.nix
|
||||
|
||||
./services/postgresql.nix
|
||||
./services/jellyfin.nix
|
||||
@ -26,8 +28,6 @@
|
||||
./services/qbittorrent.nix
|
||||
./services/bitmagnet.nix
|
||||
|
||||
# ./services/matrix.nix
|
||||
# ./services/owntracks.nix
|
||||
./services/soulseek.nix
|
||||
|
||||
./services/llama-cpp.nix
|
||||
@ -111,16 +111,18 @@
|
||||
};
|
||||
|
||||
system.activationScripts = {
|
||||
# extract all my secureboot keys
|
||||
# TODO! awful secrets management, it's globally readable in /nix/store
|
||||
"secureboot-keys".text = ''
|
||||
#!/bin/sh
|
||||
rm -fr ${config.boot.lanzaboote.pkiBundle} || true
|
||||
mkdir -p ${config.boot.lanzaboote.pkiBundle}
|
||||
${pkgs.gnutar}/bin/tar xf ${./secrets/secureboot.tar} -C ${config.boot.lanzaboote.pkiBundle}
|
||||
chown -R root:wheel ${config.boot.lanzaboote.pkiBundle}
|
||||
chmod -R 500 ${config.boot.lanzaboote.pkiBundle}
|
||||
'';
|
||||
# extract secureboot keys from agenix-decrypted tar
|
||||
"secureboot-keys" = {
|
||||
deps = [ "agenix" ];
|
||||
text = ''
|
||||
#!/bin/sh
|
||||
rm -fr ${config.boot.lanzaboote.pkiBundle} || true
|
||||
mkdir -p ${config.boot.lanzaboote.pkiBundle}
|
||||
${pkgs.gnutar}/bin/tar xf ${config.age.secrets.secureboot-tar.path} -C ${config.boot.lanzaboote.pkiBundle}
|
||||
chown -R root:wheel ${config.boot.lanzaboote.pkiBundle}
|
||||
chmod -R 500 ${config.boot.lanzaboote.pkiBundle}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
environment.etc = {
|
||||
@ -286,7 +288,7 @@
|
||||
];
|
||||
|
||||
# TODO! use proper secrets management
|
||||
hashedPassword = lib.strings.trim (builtins.readFile ./secrets/hashedPass);
|
||||
hashedPasswordFile = config.age.secrets.hashedPass.path;
|
||||
|
||||
openssh.authorizedKeys.keys = [
|
||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO4jL6gYOunUlUtPvGdML0cpbKSsPNqQ1jit4E7U1RyH" # laptop
|
||||
|
||||
88
flake.lock
generated
88
flake.lock
generated
@ -1,5 +1,28 @@
|
||||
{
|
||||
"nodes": {
|
||||
"agenix": {
|
||||
"inputs": {
|
||||
"darwin": "darwin",
|
||||
"home-manager": "home-manager",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1754433428,
|
||||
"narHash": "sha256-NA/FT2hVhKDftbHSwVnoRTFhes62+7dxZbxj5Gxvghs=",
|
||||
"owner": "ryantm",
|
||||
"repo": "agenix",
|
||||
"rev": "9edb1787864c4f59ae5074ad498b6272b3ec308d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ryantm",
|
||||
"repo": "agenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1754269165,
|
||||
@ -15,6 +38,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"darwin": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"agenix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744478979,
|
||||
"narHash": "sha256-dyN+teG9G82G+m+PX/aSAagkC+vUv0SgUw3XkPhQodQ=",
|
||||
"owner": "lnl7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "43975d782b418ebf4969e9ccba82466728c2851b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "lnl7",
|
||||
"ref": "master",
|
||||
"repo": "nix-darwin",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"deploy-rs": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
@ -146,7 +191,7 @@
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
@ -185,6 +230,27 @@
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"agenix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745494811,
|
||||
"narHash": "sha256-YZCh2o9Ua1n9uCvrvi5pRxtuVNml8X2a03qIFfRKpFs=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "abfad3d2958c9e6300a883bd443512c55dfeb1be",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
@ -360,9 +426,10 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"agenix": "agenix",
|
||||
"deploy-rs": "deploy-rs",
|
||||
"disko": "disko",
|
||||
"home-manager": "home-manager",
|
||||
"home-manager": "home-manager_2",
|
||||
"impermanence": "impermanence",
|
||||
"lanzaboote": "lanzaboote",
|
||||
"llamacpp": "llamacpp",
|
||||
@ -463,6 +530,21 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"trackerlist": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@ -481,7 +563,7 @@
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
|
||||
14
flake.nix
14
flake.nix
@ -47,6 +47,11 @@
|
||||
url = "github:nix-community/impermanence";
|
||||
};
|
||||
|
||||
agenix = {
|
||||
url = "github:ryantm/agenix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
senior_project-website = {
|
||||
url = "github:Titaniumtown/senior-project-website";
|
||||
flake = false;
|
||||
@ -76,6 +81,7 @@
|
||||
srvos,
|
||||
deploy-rs,
|
||||
impermanence,
|
||||
agenix,
|
||||
...
|
||||
}@inputs:
|
||||
let
|
||||
@ -97,7 +103,6 @@
|
||||
jellyfin = 8096; # no services.jellyfin option for this
|
||||
torrent = 6011;
|
||||
bitmagnet = 3333;
|
||||
owntracks = 3825;
|
||||
gitea = 2283;
|
||||
immich = 2284;
|
||||
soulseek_web = 5030;
|
||||
@ -110,7 +115,6 @@
|
||||
certs = services_dir + "/http_certs";
|
||||
domain = "gardling.com";
|
||||
wg_ip = "192.168.15.1";
|
||||
matrix_hostname = "matrix.${service_configs.https.domain}";
|
||||
};
|
||||
|
||||
gitea = {
|
||||
@ -142,10 +146,6 @@
|
||||
cacheDir = services_dir + "/jellyfin_cache";
|
||||
};
|
||||
|
||||
owntracks = {
|
||||
data_dir = services_dir + "/owntracks";
|
||||
};
|
||||
|
||||
slskd = rec {
|
||||
base = "/var/lib/slskd";
|
||||
downloads = base + "/downloads";
|
||||
@ -221,6 +221,8 @@
|
||||
|
||||
lanzaboote.nixosModules.lanzaboote
|
||||
|
||||
agenix.nixosModules.default
|
||||
|
||||
home-manager.nixosModules.home-manager
|
||||
(
|
||||
{
|
||||
|
||||
22
secrets.nix
Normal file
22
secrets.nix
Normal file
@ -0,0 +1,22 @@
|
||||
let
|
||||
# USB secrets key - for encrypting/decrypting all secrets
|
||||
usbSecretsKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN8+eSX2LH5wEHVG9sSv97ceD5zdTarV0lRvoUso4A7p USB secrets decryption key";
|
||||
in
|
||||
{
|
||||
# ZFS encryption key
|
||||
"zfs-key.age".publicKeys = [ usbSecretsKey ];
|
||||
|
||||
# Secureboot keys archive
|
||||
"secureboot.tar.age".publicKeys = [ usbSecretsKey ];
|
||||
|
||||
# System passwords and auth
|
||||
"hashedPass.age".publicKeys = [ usbSecretsKey ];
|
||||
|
||||
# Service authentication
|
||||
"caddy_auth.age".publicKeys = [ usbSecretsKey ];
|
||||
"jellyfin-api-key.age".publicKeys = [ usbSecretsKey ];
|
||||
"slskd_env.age".publicKeys = [ usbSecretsKey ];
|
||||
|
||||
# Network configuration
|
||||
"wg0.conf.age".publicKeys = [ usbSecretsKey ];
|
||||
}
|
||||
Binary file not shown.
BIN
secrets/caddy_auth.age
Normal file
BIN
secrets/caddy_auth.age
Normal file
Binary file not shown.
Binary file not shown.
BIN
secrets/hashedPass.age
Normal file
BIN
secrets/hashedPass.age
Normal file
Binary file not shown.
Binary file not shown.
BIN
secrets/jellyfin-api-key.age
Normal file
BIN
secrets/jellyfin-api-key.age
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
secrets/secureboot.tar.age
Normal file
BIN
secrets/secureboot.tar.age
Normal file
Binary file not shown.
BIN
secrets/slskd_env.age
Normal file
BIN
secrets/slskd_env.age
Normal file
Binary file not shown.
BIN
secrets/wg0.conf
BIN
secrets/wg0.conf
Binary file not shown.
BIN
secrets/wg0.conf.age
Normal file
BIN
secrets/wg0.conf.age
Normal file
Binary file not shown.
BIN
secrets/zfs-key
BIN
secrets/zfs-key
Binary file not shown.
BIN
secrets/zfs-key.age
Normal file
BIN
secrets/zfs-key.age
Normal file
Binary file not shown.
@ -25,7 +25,7 @@
|
||||
};
|
||||
|
||||
services.caddy.virtualHosts."bitmagnet.${service_configs.https.domain}".extraConfig = ''
|
||||
${builtins.readFile ../secrets/caddy_auth}
|
||||
import ${config.age.secrets.caddy_auth.path}
|
||||
reverse_proxy ${service_configs.https.wg_ip}:${builtins.toString service_configs.ports.bitmagnet}
|
||||
'';
|
||||
}
|
||||
|
||||
@ -66,6 +66,12 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
# Add agenix dependency for caddy service
|
||||
systemd.services.caddy = {
|
||||
after = [ "agenix.service" ];
|
||||
requires = [ "agenix.service" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${config.services.caddy.dataDir} 700 ${config.services.caddy.user} ${config.services.caddy.group}"
|
||||
];
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
systemd.services.llama-cpp.serviceConfig.DynamicUser = lib.mkForce false;
|
||||
|
||||
services.caddy.virtualHosts."llm.${service_configs.https.domain}".extraConfig = ''
|
||||
${builtins.readFile ../secrets/caddy_auth}
|
||||
import ${config.age.secrets.caddy_auth.path}
|
||||
reverse_proxy :${builtins.toString config.services.llama-cpp.port}
|
||||
'';
|
||||
}
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
service_configs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.matrix-conduit.settings.global.registration_token =
|
||||
builtins.readFile ../secrets/matrix_reg_token;
|
||||
|
||||
services.caddy.virtualHosts.${service_configs.https.domain}.extraConfig = lib.mkBefore ''
|
||||
header /.well-known/matrix/* Content-Type application/json
|
||||
header /.well-known/matrix/* Access-Control-Allow-Origin *
|
||||
respond /.well-known/matrix/server `{"m.server": "${service_configs.https.matrix_hostname}:${service_configs.ports.https}"}`
|
||||
respond /.well-known/matrix/client `{"m.server":{"base_url":"https://${service_configs.https.matrix_hostname}"},"m.homeserver":{"base_url":"https://${service_configs.https.matrix_hostname}"},"org.matrix.msc3575.proxy":{"base_url":"https://${config.services.matrix-conduit.settings.global.server_name}"}}`
|
||||
'';
|
||||
|
||||
services.caddy.virtualHosts."${service_configs.https.matrix_hostname}".extraConfig = ''
|
||||
reverse_proxy :${builtins.toString config.services.matrix-conduit.settings.global.port}
|
||||
'';
|
||||
|
||||
# Exact duplicate
|
||||
services.caddy.virtualHosts."${service_configs.https.matrix_hostname}:8448".extraConfig =
|
||||
config.services.caddy.virtualHosts."${config.services.matrix-conduit.settings.global.server_name
|
||||
}".extraConfig;
|
||||
|
||||
services.matrix-conduit = {
|
||||
enable = true;
|
||||
package = pkgs.conduwuit;
|
||||
|
||||
settings.global = {
|
||||
port = 6167;
|
||||
server_name = service_configs.https.domain;
|
||||
database_backend = "rocksdb";
|
||||
allow_registration = true;
|
||||
|
||||
new_user_displayname_suffix = "";
|
||||
|
||||
trusted_servers = [
|
||||
"matrix.org"
|
||||
"constellatory.net"
|
||||
"tchncs.de"
|
||||
"envs.net"
|
||||
];
|
||||
|
||||
# without this, conduit fails to start
|
||||
address = "0.0.0.0";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"Z /var/lib/private/matrix-conduit 0770 conduit conduit"
|
||||
];
|
||||
|
||||
# for federation
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
8448
|
||||
];
|
||||
|
||||
# for federation
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
8448
|
||||
];
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
{
|
||||
pkgs,
|
||||
service_configs,
|
||||
username,
|
||||
...
|
||||
}:
|
||||
let
|
||||
owntracks_pkg = pkgs.owntracks-recorder.overrideAttrs (old: {
|
||||
installPhase = old.installPhase + ''
|
||||
mkdir -p $out/usr/share/ot-recorder
|
||||
cp -R docroot/* $out/usr/share/ot-recorder'';
|
||||
});
|
||||
in
|
||||
{
|
||||
users.groups.owntracks = { };
|
||||
users.users.owntracks = {
|
||||
isNormalUser = true;
|
||||
group = "owntracks";
|
||||
};
|
||||
|
||||
systemd.services.owntracks = {
|
||||
enable = true;
|
||||
description = "Store and access data published by OwnTracks apps";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
User = "owntracks";
|
||||
Group = "owntracks";
|
||||
WorkingDirectory = "${owntracks_pkg}";
|
||||
ExecStart = "${owntracks_pkg}/bin/ot-recorder -S ${service_configs.owntracks.data_dir} --doc-root usr/share/ot-recorder --http-port ${builtins.toString service_configs.ports.owntracks} --port 0";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"Z ${service_configs.owntracks.data_dir} 0770 owntracks owntracks"
|
||||
];
|
||||
|
||||
services.caddy.virtualHosts."owntracks.${service_configs.https.domain}".extraConfig = ''
|
||||
${builtins.readFile ../secrets/owntracks_caddy_auth}
|
||||
reverse_proxy :${builtins.toString service_configs.ports.owntracks}
|
||||
'';
|
||||
|
||||
users.users.${username}.extraGroups = [
|
||||
"owntracks"
|
||||
];
|
||||
}
|
||||
@ -102,7 +102,7 @@
|
||||
];
|
||||
|
||||
services.caddy.virtualHosts."torrent.${service_configs.https.domain}".extraConfig = ''
|
||||
${builtins.readFile ../secrets/caddy_auth}
|
||||
import ${config.age.secrets.caddy_auth.path}
|
||||
reverse_proxy ${service_configs.https.wg_ip}:${builtins.toString config.services.qbittorrent.webuiPort}
|
||||
'';
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ in
|
||||
"skskd_env".text = ''
|
||||
#!/bin/sh
|
||||
rm -fr ${slskd_env} || true
|
||||
cp ${../secrets/slskd_env} ${slskd_env}
|
||||
cp ${config.age.secrets.slskd_env.path} ${slskd_env}
|
||||
chmod 0500 ${slskd_env}
|
||||
chown ${config.services.slskd.user}:${config.services.slskd.group} ${slskd_env}
|
||||
'';
|
||||
@ -67,6 +67,12 @@ in
|
||||
users.users.${config.services.jellyfin.user}.extraGroups = [ "music" ];
|
||||
users.users.${username}.extraGroups = [ "music" ];
|
||||
|
||||
# Add agenix dependencies for slskd service
|
||||
systemd.services.slskd = {
|
||||
after = [ "agenix.service" ];
|
||||
requires = [ "agenix.service" ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"Z ${service_configs.music_dir} 0750 ${username} music"
|
||||
"Z ${service_configs.slskd.base} 0750 ${config.services.slskd.user} ${config.services.slskd.group}"
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
pkgs,
|
||||
service_configs,
|
||||
eth_interface,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# network namespace that is proxied through mullvad
|
||||
vpnNamespaces.wg = {
|
||||
enable = true;
|
||||
wireguardConfigFile = ../secrets/wg0.conf;
|
||||
wireguardConfigFile = config.age.secrets.wg0-conf.path;
|
||||
accessibleFrom = [
|
||||
# "192.168.0.0/24"
|
||||
];
|
||||
@ -20,13 +21,15 @@
|
||||
"network.target"
|
||||
"jellyfin.service"
|
||||
"qbittorrent.service"
|
||||
"agenix.service"
|
||||
];
|
||||
requires = [ "agenix.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = pkgs.writeShellScript "jellyfin-monitor-start" ''
|
||||
export JELLYFIN_API_KEY=$(cat ${../secrets/jellyfin-api-key})
|
||||
export JELLYFIN_API_KEY=$(cat ${config.age.secrets.jellyfin-api-key.path})
|
||||
exec ${
|
||||
pkgs.python3.withPackages (ps: with ps; [ requests ])
|
||||
}/bin/python ${./jellyfin-qbittorrent-monitor.py}
|
||||
|
||||
58
usb-secrets.nix
Normal file
58
usb-secrets.nix
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Extract USB secrets key in main system before agenix
|
||||
systemd.services.usb-secrets = {
|
||||
description = "Extract USB secrets key";
|
||||
wantedBy = [ "sysinit.target" ];
|
||||
before = [ "agenix.service" ];
|
||||
wants = [ "local-fs.target" ];
|
||||
after = [ "local-fs.target" ];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
};
|
||||
script = ''
|
||||
mkdir -p /run/secrets /mnt/usb
|
||||
|
||||
# Check if key already exists
|
||||
if [ -f /run/secrets/usb-secrets-key ]; then
|
||||
echo "USB secrets key already loaded"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Wait for USB devices
|
||||
for i in {1..30}; do
|
||||
[ -e /dev/disk/by-label/SECRETS ] && break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Mount USB and copy key
|
||||
if mount /dev/disk/by-label/SECRETS /mnt/usb 2>/dev/null; then
|
||||
if [ -f /mnt/usb/usb-secrets-key ]; then
|
||||
install -m 600 /mnt/usb/usb-secrets-key /run/secrets/usb-secrets-key
|
||||
umount /mnt/usb
|
||||
echo "USB secrets key loaded"
|
||||
else
|
||||
umount /mnt/usb
|
||||
echo "Key file not found"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "USB not found"
|
||||
exit 1
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
age.identityPaths = [ "/run/secrets/usb-secrets-key" ];
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /run/secrets 0700 root root -"
|
||||
];
|
||||
}
|
||||
44
usb-secrets/setup-usb.sh
Executable file
44
usb-secrets/setup-usb.sh
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p parted dosfstools
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
USB_DEVICE="$1"
|
||||
if [[ -z "${USB_DEVICE:-}" ]]; then
|
||||
echo "Usage: $0 <usb_device>"
|
||||
echo "Example: $0 /dev/sdb"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -b "$USB_DEVICE" ]]; then
|
||||
echo "Error: $USB_DEVICE is not a block device"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SCRIPT_DIR/usb-secrets/usb-secrets-key" ]]; then
|
||||
echo "Error: usb-secrets-key not found at $SCRIPT_DIR/usb-secrets/usb-secrets-key"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "WARNING: This will completely wipe $USB_DEVICE"
|
||||
echo "Press Ctrl+C to abort, or Enter to continue..."
|
||||
read
|
||||
|
||||
echo "Creating partition and formatting as FAT32..."
|
||||
parted -s "$USB_DEVICE" mklabel msdos
|
||||
parted -s "$USB_DEVICE" mkpart primary fat32 0% 100%
|
||||
parted -s "$USB_DEVICE" set 1 boot on
|
||||
|
||||
USB_PARTITION="${USB_DEVICE}1"
|
||||
mkfs.fat -F 32 -n "SECRETS" "$USB_PARTITION"
|
||||
|
||||
echo "Copying key to USB..."
|
||||
MOUNT_POINT=$(mktemp -d)
|
||||
trap "umount $MOUNT_POINT 2>/dev/null || true; rmdir $MOUNT_POINT" EXIT
|
||||
|
||||
mount "$USB_PARTITION" "$MOUNT_POINT"
|
||||
cp "$SCRIPT_DIR/usb-secrets/usb-secrets-key" "$MOUNT_POINT/"
|
||||
umount "$MOUNT_POINT"
|
||||
|
||||
echo "USB setup complete! Label: SECRETS"
|
||||
echo "Create multiple backup USB keys for redundancy."
|
||||
BIN
usb-secrets/usb-secrets/usb-secrets-key
Normal file
BIN
usb-secrets/usb-secrets/usb-secrets-key
Normal file
Binary file not shown.
1
usb-secrets/usb-secrets/usb-secrets-key.pub
Normal file
1
usb-secrets/usb-secrets/usb-secrets-key.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN8+eSX2LH5wEHVG9sSv97ceD5zdTarV0lRvoUso4A7p USB secrets decryption key
|
||||
12
zfs.nix
12
zfs.nix
@ -1,4 +1,5 @@
|
||||
{
|
||||
config,
|
||||
service_configs,
|
||||
pkgs,
|
||||
...
|
||||
@ -10,13 +11,14 @@ let
|
||||
in
|
||||
{
|
||||
system.activationScripts = {
|
||||
# TODO! replace with proper secrets management
|
||||
# Copy decrypted ZFS key from agenix to expected location
|
||||
# /etc is on tmpfs due to impermanence, so no persistent storage risk
|
||||
"zfs-key".text = ''
|
||||
#!/bin/sh
|
||||
rm -fr ${zfs-key} || true
|
||||
cp ${./secrets/zfs-key} ${zfs-key}
|
||||
chmod 0500 ${zfs-key}
|
||||
chown root:wheel ${zfs-key}
|
||||
rm -f ${zfs-key} || true
|
||||
cp ${config.age.secrets.zfs-key.path} ${zfs-key}
|
||||
chmod 0400 ${zfs-key}
|
||||
chown root:root ${zfs-key}
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user