Merge branch 'wip/hosts'
This commit is contained in:
commit
b0c5a5907f
12
flake.lock
12
flake.lock
|
@ -60,22 +60,22 @@
|
|||
},
|
||||
"locked": {
|
||||
"lastModified": 1,
|
||||
"narHash": "sha256-m+zWb62u+aq/x6VX82khojR2GNhWRIAo+csqaN1ixGA=",
|
||||
"path": "/nix/store/l3d7c7jglbgslp60cwpp3sikwd9vlr1q-source/nixpatches",
|
||||
"narHash": "sha256-5zCxdHGOS0OOP7vbgTA1iwv9GVr5JSiths7QmgUsU84=",
|
||||
"path": "/nix/store/9a5k9pfawxzz1sng17si26sc9af39jr1-source/nixpatches",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"path": "/nix/store/l3d7c7jglbgslp60cwpp3sikwd9vlr1q-source/nixpatches",
|
||||
"path": "/nix/store/9a5k9pfawxzz1sng17si26sc9af39jr1-source/nixpatches",
|
||||
"type": "path"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1673957332,
|
||||
"narHash": "sha256-njH7Szk1BLVWGMw7IRibgGejSlxXHj9saZHfH20gHdk=",
|
||||
"lastModified": 1673800717,
|
||||
"narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b83e7f5a04a3acc8e92228b0c4bae68933d504eb",
|
||||
"rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."desko".wg-home.ip;
|
||||
sane.services.duplicity.enable = true;
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../secrets/desko.yaml;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/desko.yaml;
|
||||
sane.persist.enable = true;
|
||||
|
||||
sane.gui.sway.enable = true;
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
|
@ -19,7 +23,7 @@
|
|||
services.usbmuxd.enable = true;
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
||||
|
@ -41,7 +45,7 @@
|
|||
};
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/desko.yaml;
|
||||
sopsFile = ../../../secrets/desko.yaml;
|
||||
};
|
||||
|
||||
programs.steam = {
|
|
@ -1,9 +1,13 @@
|
|||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."lappy".wg-home.ip;
|
||||
|
||||
# sane.packages.enableDevPkgs = true;
|
||||
|
||||
# sane.users.guest.enable = true;
|
||||
|
@ -14,7 +18,7 @@
|
|||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/lappy.yaml;
|
||||
sopsFile = ../../../secrets/lappy.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
|
@ -6,6 +6,11 @@
|
|||
./kernel.nix
|
||||
];
|
||||
|
||||
sane.roles.client = true;
|
||||
# TODO
|
||||
# sane.services.wg-home.enable = true;
|
||||
# sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip;
|
||||
|
||||
# cross-compiled documentation is *slow*.
|
||||
# no obvious way to natively compile docs (2022/09/29).
|
||||
# entrypoint is nixos/modules/misc/documentation.nix
|
||||
|
@ -19,7 +24,7 @@
|
|||
services.getty.autologinUser = "root"; # allows for emergency maintenance?
|
||||
|
||||
sops.secrets.colin-passwd = {
|
||||
sopsFile = ../../secrets/moby.yaml;
|
||||
sopsFile = ../../../secrets/moby.yaml;
|
||||
neededForUsers = true;
|
||||
};
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
{ pkgs, ... }:
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./fs.nix
|
||||
./net.nix
|
||||
./users.nix
|
||||
./secrets.nix
|
||||
./services
|
||||
];
|
||||
|
||||
|
@ -16,15 +17,13 @@
|
|||
];
|
||||
sane.persist.enable = true;
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
|
||||
boot.loader.efi.canTouchEfiVariables = false;
|
||||
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
|
||||
|
||||
sops.secrets.duplicity_passphrase = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
# both transmission and ipfs try to set different net defaults.
|
||||
# we just use the most aggressive of the two here:
|
||||
boot.kernel.sysctl = {
|
|
@ -163,45 +163,6 @@
|
|||
'';
|
||||
networking.iproute2.enable = true;
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
|
||||
# host a wireguard VPN which allows access to other wg clients and forwards to internet
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
privateKeyFile = config.sops.secrets.wg_home_privkey.path;
|
||||
listenPort = 51820;
|
||||
ips = [
|
||||
"10.0.10.5/24"
|
||||
];
|
||||
peers = [
|
||||
{
|
||||
# peers and host all use the same key
|
||||
publicKey = "pWtnKW7f7sNIZQ2M83uJ7cHg3IL1tebE3IoVkCgjkXM=";
|
||||
allowedIPs = [ "10.0.10.0/24" ];
|
||||
# allowedIPs = [ "10.0.10.0/24" "192.168.0.0/24" ];
|
||||
# allowedIPs = [ "0.0.0.0/0" ];
|
||||
}
|
||||
# {
|
||||
# # lappy
|
||||
# publicKey = "TODO";
|
||||
# allowedIPs = [ "10.0.10.20/32" ];
|
||||
# }
|
||||
# {
|
||||
# # desko
|
||||
# publicKey = "TODO";
|
||||
# allowedIPs = [ "10.0.10.22/32" ];
|
||||
# }
|
||||
# {
|
||||
# # moby
|
||||
# publicKey = "TODO";
|
||||
# allowedIPs = [ "10.0.10.48/32" ];
|
||||
# }
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
# HURRICANE ELECTRIC CONFIG:
|
||||
# networking.sits = {
|
41
hosts/by-name/servo/secrets.nix
Normal file
41
hosts/by-name/servo/secrets.nix
Normal file
|
@ -0,0 +1,41 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."duplicity_passphrase" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
sopsFile = ../../../secrets/servo/mautrix_signal_env.bin;
|
||||
};
|
||||
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."wg_ovpns_privkey" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
|
@ -24,8 +24,4 @@ lib.mkIf false
|
|||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."ddns_afraid" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
|
@ -27,8 +27,4 @@ lib.mkIf false
|
|||
OnUnitActiveSec = "10min";
|
||||
};
|
||||
};
|
||||
|
||||
sops.secrets."ddns_he" = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
}
|
|
@ -11,8 +11,7 @@
|
|||
|
||||
{ config, lib, pkgs, sane-lib, ... }:
|
||||
{
|
||||
sops.secrets.freshrss_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."freshrss_passwd" = {
|
||||
owner = config.users.users.freshrss.name;
|
||||
mode = "0400";
|
||||
};
|
|
@ -131,8 +131,7 @@
|
|||
};
|
||||
|
||||
|
||||
sops.secrets.matrix_synapse_secrets = {
|
||||
sopsFile = ../../../../secrets/servo.yaml;
|
||||
sops.secrets."matrix_synapse_secrets" = {
|
||||
owner = config.users.users.matrix-synapse.name;
|
||||
};
|
||||
}
|
|
@ -25,8 +25,7 @@
|
|||
{ user = "mautrix-signal"; group = "mautrix-signal"; directory = "/var/lib/mautrix-signal"; }
|
||||
];
|
||||
|
||||
sops.secrets.mautrix_signal_env = {
|
||||
sopsFile = ../../../../secrets/servo/mautrix_signal_env.bin;
|
||||
sops.secrets."mautrix_signal_env" = {
|
||||
format = "binary";
|
||||
mode = "0440";
|
||||
owner = config.users.users.mautrix-signal.name;
|
|
@ -17,5 +17,5 @@
|
|||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = "native";
|
||||
|
||||
sane.services.nixserve.enable = true;
|
||||
sane.services.nixserve.sopsFile = ../../../secrets/servo.yaml;
|
||||
sane.services.nixserve.sopsFile = ../../../../secrets/servo.yaml;
|
||||
}
|
|
@ -179,8 +179,7 @@
|
|||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = "native";
|
||||
|
||||
sops.secrets.pleroma_secrets = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."pleroma_secrets" = {
|
||||
owner = config.users.users.pleroma.name;
|
||||
};
|
||||
}
|
|
@ -197,8 +197,7 @@ in
|
|||
# }
|
||||
];
|
||||
|
||||
sops.secrets.dovecot_passwd = {
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
sops.secrets."dovecot_passwd" = {
|
||||
owner = config.users.users.dovecot2.name;
|
||||
# TODO: debug why mail can't be sent without this being world-readable
|
||||
mode = "0444";
|
|
@ -8,7 +8,6 @@ lib.mkIf false
|
|||
{
|
||||
sops.secrets."mediawiki_pw" = {
|
||||
owner = config.users.users.mediawiki.name;
|
||||
sopsFile = ../../../secrets/servo.yaml;
|
||||
};
|
||||
|
||||
services.mediawiki.enable = true;
|
|
@ -1,16 +0,0 @@
|
|||
{ lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
# persist external pairings by default
|
||||
sane.persist.sys.plaintext = [ "/var/lib/bluetooth" ];
|
||||
|
||||
sane.fs."/var/lib/bluetooth".generated.acl.mode = "0700";
|
||||
sane.fs."/var/lib/bluetooth/.secrets.stamp" = {
|
||||
wantedBeforeBy = [ "bluetooth.service" ];
|
||||
# XXX: install-bluetooth uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../scripts/install-bluetooth + ''
|
||||
touch "/var/lib/bluetooth/.secrets.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/bt" ];
|
||||
};
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
./bluetooth.nix
|
||||
./cross.nix
|
||||
./feeds.nix
|
||||
./fs.nix
|
||||
./hardware
|
||||
./hardware.nix
|
||||
./i2p.nix
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
# if using router's DNS, these mappings will already exist.
|
||||
# if using a different DNS provider (which servo does), then we need to explicity provide them.
|
||||
# ugly hack. would be better to get servo to somehow use the router's DNS
|
||||
networking.hosts = {
|
||||
"192.168.0.5" = [ "servo" ];
|
||||
"192.168.0.20" = [ "lappy" ];
|
||||
"192.168.0.22" = [ "desko" ];
|
||||
"192.168.0.48" = [ "moby" ];
|
||||
};
|
||||
|
||||
# the default backend is "wpa_supplicant".
|
||||
# wpa_supplicant reliably picks weak APs to connect to.
|
||||
# see: <https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/474>
|
||||
|
@ -30,38 +20,4 @@
|
|||
General.RoamThreshold = "-52"; # default -70
|
||||
General.RoamThreshold5G = "-52"; # default -76
|
||||
};
|
||||
|
||||
sane.fs."/var/lib/iwd/.secrets.psk.stamp" = {
|
||||
wantedBeforeBy = [ "iwd.service" ];
|
||||
generated.acl.mode = "0600";
|
||||
# XXX: install-iwd uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../scripts/install-iwd + ''
|
||||
touch "/var/lib/iwd/.secrets.psk.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/iwd" "/var/lib/iwd" ];
|
||||
};
|
||||
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
# TODO: remove this hacky `if` block
|
||||
networking.wireguard.interfaces.wg-home = lib.mkIf (config.networking.hostName != "servo") {
|
||||
privateKeyFile = config.sops.secrets.wg_home_privkey.path;
|
||||
# client IP (TODO: make host-specific)
|
||||
ips = [ "10.100.0.20/32" ];
|
||||
listenPort = 51820; # to match firewall allowedUDPPorts (without this wg uses random port numbers)
|
||||
|
||||
peers = [
|
||||
{
|
||||
# server pubkey
|
||||
publicKey = "pWtnKW7f7sNIZQ2M83uJ7cHg3IL1tebE3IoVkCgjkXM=";
|
||||
|
||||
# accept traffic from any IP addr on the other side of the tunnel
|
||||
allowedIPs = [ "0.0.0.0/0" ];
|
||||
|
||||
endpoint = "uninsane.org:51820";
|
||||
|
||||
# send keepalives every 25 seconds to keep NAT routes live
|
||||
persistentKeepalive = 25;
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@
|
|||
sops.secrets."wg_ovpnd_ukr_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
sops.secrets."wg_home_privkey" = {
|
||||
sopsFile = ../../secrets/universal.yaml;
|
||||
};
|
||||
|
||||
sops.secrets."snippets" = {
|
||||
sopsFile = ../../secrets/universal/snippets.bin;
|
||||
|
|
|
@ -1,24 +1,33 @@
|
|||
{ config, lib, sane-data, sane-lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) head map mapAttrs tail;
|
||||
inherit (lib) concatStringsSep mkMerge reverseList;
|
||||
in
|
||||
{
|
||||
sane.ssh.pubkeys =
|
||||
let
|
||||
# path is a DNS-style path like [ "org" "uninsane" "root" ]
|
||||
keyNameForPath = path:
|
||||
let
|
||||
rev = lib.reverseList path;
|
||||
name = builtins.head rev;
|
||||
host = lib.concatStringsSep "." (builtins.tail rev);
|
||||
rev = reverseList path;
|
||||
name = head rev;
|
||||
host = concatStringsSep "." (tail rev);
|
||||
in
|
||||
"${name}@${host}";
|
||||
|
||||
# [{ path :: [String], value :: String }] for the keys we want to install
|
||||
globalKeys = sane-lib.flattenAttrs sane-data.keys;
|
||||
localKeys = sane-lib.flattenAttrs sane-data.keys.org.uninsane.local;
|
||||
in lib.mkMerge (builtins.map
|
||||
domainKeys = sane-lib.flattenAttrs (
|
||||
mapAttrs (host: cfg: {
|
||||
colin = cfg.ssh.user_pubkey;
|
||||
root = cfg.ssh.host_pubkey;
|
||||
}) config.sane.hosts.by-name
|
||||
);
|
||||
in mkMerge (map
|
||||
({ path, value }: {
|
||||
"${keyNameForPath path}" = value;
|
||||
"${keyNameForPath path}" = lib.mkIf (value != null) value;
|
||||
})
|
||||
(globalKeys ++ localKeys)
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
|
||||
|
||||
# args from flake-level `import`
|
||||
{ hostName, localSystem }:
|
||||
{ ... }:
|
||||
|
||||
# module args
|
||||
{ config, ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./${hostName}
|
||||
./by-name/${hostName}
|
||||
./common
|
||||
./modules
|
||||
];
|
||||
|
||||
networking.hostName = hostName;
|
||||
|
|
12
hosts/modules/default.nix
Normal file
12
hosts/modules/default.nix
Normal file
|
@ -0,0 +1,12 @@
|
|||
{ ... }:
|
||||
|
||||
{
|
||||
imports = [
|
||||
./derived-secrets.nix
|
||||
./hardware
|
||||
./hostnames.nix
|
||||
./hosts.nix
|
||||
./roles
|
||||
./wg-home.nix
|
||||
];
|
||||
}
|
47
hosts/modules/derived-secrets.nix
Normal file
47
hosts/modules/derived-secrets.nix
Normal file
|
@ -0,0 +1,47 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) toString;
|
||||
inherit (lib) mapAttrs mkOption types;
|
||||
cfg = config.sane.derived-secrets;
|
||||
secret = types.submodule {
|
||||
options = {
|
||||
len = mkOption {
|
||||
type = types.int;
|
||||
};
|
||||
encoding = mkOption {
|
||||
type = types.enum [ "base64" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.derived-secrets = mkOption {
|
||||
type = types.attrsOf secret;
|
||||
default = {};
|
||||
description = ''
|
||||
fs path => secret options.
|
||||
for each entry, we create an item at the given path whose value is deterministic,
|
||||
but also pseudo-random and not predictable by anyone without root access to the machine.
|
||||
as PRNG source we use the host ssh key, and derived secrets are salted based on the destination path.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.fs = mapAttrs (path: c: {
|
||||
generated.script.script = ''
|
||||
echo "$1" | cat /dev/stdin /etc/ssh/host_keys/ssh_host_ed25519_key \
|
||||
| sha512sum \
|
||||
| cut -c 1-${toString (c.len * 2)} \
|
||||
| tr a-z A-Z \
|
||||
| basenc -d --base16 \
|
||||
| basenc --${c.encoding} \
|
||||
> "$1"
|
||||
'';
|
||||
generated.script.scriptArgs = [ path ];
|
||||
generated.acl.mode = "0600";
|
||||
}) cfg;
|
||||
};
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
{
|
||||
imports = [
|
||||
./all.nix
|
||||
./x86_64.nix
|
||||
];
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
{ lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
config = mkIf (pkgs.system == "x86_64-linux") {
|
||||
config = lib.mkIf (pkgs.system == "x86_64-linux") {
|
||||
boot.initrd.availableKernelModules = [
|
||||
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
|
||||
"usb_storage" # rpi needed this to boot from usb storage, i think.
|
11
hosts/modules/hostnames.nix
Normal file
11
hosts/modules/hostnames.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# if using router's DNS, these mappings will already exist.
|
||||
# if using a different DNS provider (which servo does), then we need to explicity provide them.
|
||||
# ugly hack. would be better to get servo to somehow use the router's DNS
|
||||
networking.hosts = lib.mapAttrs' (host: cfg: {
|
||||
name = cfg.lan-ip;
|
||||
value = [ host ];
|
||||
}) config.sane.hosts.by-name;
|
||||
}
|
98
hosts/modules/hosts.nix
Normal file
98
hosts/modules/hosts.nix
Normal file
|
@ -0,0 +1,98 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) attrValues filterAttrs mkMerge mkOption types;
|
||||
cfg = config.sane.hosts;
|
||||
|
||||
host = types.submodule ({ config, ... }: {
|
||||
options = {
|
||||
ssh.user_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey that the primary user of this machine will use when connecting to other machines.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
ssh.host_pubkey = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ssh pubkey which this host will present to connections initiated against it.
|
||||
e.g. "ssh-ed25519 AAAA<base64>".
|
||||
'';
|
||||
};
|
||||
wg-home.pubkey = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
wireguard public key for the wg-home VPN.
|
||||
e.g. "pWtnKW7f7sNIZQ2M83uJ7cHg3IL1tebE3IoVkCgjkXM=".
|
||||
'';
|
||||
};
|
||||
wg-home.ip = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
IP address to use on the wg-home VPN.
|
||||
e.g. "10.0.10.5";
|
||||
'';
|
||||
};
|
||||
wg-home.endpoint = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
lan-ip = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
ip address when on the lan.
|
||||
e.g. "192.168.0.5";
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.hosts.by-name = mkOption {
|
||||
type = types.attrsOf host;
|
||||
default = {};
|
||||
description = ''
|
||||
map of hostname => attrset of information specific to that host,
|
||||
like its ssh pubkey, etc.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# TODO: this should be populated per-host
|
||||
sane.hosts.by-name."desko" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||
wg-home.pubkey = "17PMZssYi0D4t2d0vbmhjBKe1sGsE8kT8/dod0Q2CXc=";
|
||||
wg-home.ip = "10.0.10.22";
|
||||
lan-ip = "192.168.0.22";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."lappy" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||
wg-home.pubkey = "FTUWGw2p4/cEcrrIE86PWVnqctbv8OYpw8Gt3+dC/lk=";
|
||||
wg-home.ip = "10.0.10.20";
|
||||
lan-ip = "192.168.0.20";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."moby" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||
lan-ip = "192.168.0.48";
|
||||
};
|
||||
|
||||
sane.hosts.by-name."servo" = {
|
||||
ssh.user_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||
ssh.host_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||
wg-home.pubkey = "roAw+IUFVtdpCcqa4khB385Qcv9l5JAB//730tyK4Wk=";
|
||||
wg-home.ip = "10.0.10.5";
|
||||
wg-home.endpoint = "uninsane.org:51820";
|
||||
lan-ip = "192.168.0.5";
|
||||
};
|
||||
};
|
||||
}
|
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
18
hosts/modules/roles/client/bluetooth-pairings.nix
Normal file
|
@ -0,0 +1,18 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
# persist external pairings by default
|
||||
sane.persist.sys.plaintext = [ "/var/lib/bluetooth" ];
|
||||
|
||||
sane.fs."/var/lib/bluetooth".generated.acl.mode = "0700";
|
||||
sane.fs."/var/lib/bluetooth/.secrets.stamp" = {
|
||||
wantedBeforeBy = [ "bluetooth.service" ];
|
||||
# XXX: install-bluetooth uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../../../scripts/install-bluetooth + ''
|
||||
touch "/var/lib/bluetooth/.secrets.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/bt" ];
|
||||
};
|
||||
};
|
||||
}
|
17
hosts/modules/roles/client/default.nix
Normal file
17
hosts/modules/roles/client/default.nix
Normal file
|
@ -0,0 +1,17 @@
|
|||
{ config, lib, ... }:
|
||||
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./bluetooth-pairings.nix
|
||||
./wifi-pairings.nix
|
||||
];
|
||||
|
||||
# option is consumed by the other imports in this dir
|
||||
options.sane.roles.client = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
}
|
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
15
hosts/modules/roles/client/wifi-pairings.nix
Normal file
|
@ -0,0 +1,15 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
{
|
||||
config = lib.mkIf config.sane.roles.client {
|
||||
sane.fs."/var/lib/iwd/.secrets.psk.stamp" = {
|
||||
wantedBeforeBy = [ "iwd.service" ];
|
||||
generated.acl.mode = "0600";
|
||||
# XXX: install-iwd uses sed, but that's part of the default systemd unit path, it seems
|
||||
generated.script.script = builtins.readFile ../../../../scripts/install-iwd + ''
|
||||
touch "/var/lib/iwd/.secrets.psk.stamp"
|
||||
'';
|
||||
generated.script.scriptArgs = [ "/run/secrets/iwd" "/var/lib/iwd" ];
|
||||
};
|
||||
};
|
||||
}
|
6
hosts/modules/roles/default.nix
Normal file
6
hosts/modules/roles/default.nix
Normal file
|
@ -0,0 +1,6 @@
|
|||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./client
|
||||
];
|
||||
}
|
80
hosts/modules/wg-home.nix
Normal file
80
hosts/modules/wg-home.nix
Normal file
|
@ -0,0 +1,80 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
inherit (builtins) filter map;
|
||||
inherit (lib) concatMap mapAttrsToList mkIf mkMerge mkOption optionalAttrs types;
|
||||
cfg = config.sane.services.wg-home;
|
||||
server-cfg = config.sane.hosts.by-name."servo".wg-home;
|
||||
mkPeer = { ips, pubkey, endpoint }: {
|
||||
publicKey = pubkey;
|
||||
allowedIPs = map (k: "${k}/32") ips;
|
||||
} // (optionalAttrs (endpoint != null) {
|
||||
inherit endpoint;
|
||||
# send keepalives every 25 seconds to keep NAT routes live.
|
||||
# only need to do this from client -> server though, i think.
|
||||
persistentKeepalive = 25;
|
||||
# allows wireguard to notice DNS/hostname changes, with this much effective TTL.
|
||||
dynamicEndpointRefreshSeconds = 600;
|
||||
});
|
||||
# make separate peers to route each given host
|
||||
mkClientPeers = hosts: map (p: mkPeer {
|
||||
inherit (p) pubkey endpoint;
|
||||
ips = [ p.ip ];
|
||||
}) hosts;
|
||||
# make a single peer which routes all the given hosts
|
||||
mkServerPeer = hosts: mkPeer {
|
||||
inherit (server-cfg) pubkey endpoint;
|
||||
ips = map (h: h.ip) hosts;
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.services.wg-home.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# generate a (deterministic) wireguard private key
|
||||
sane.derived-secrets."/run/wg-home.priv" = {
|
||||
len = 32;
|
||||
encoding = "base64";
|
||||
};
|
||||
|
||||
# wireguard VPN which allows everything on my domain to speak to each other even when
|
||||
# not behind a shared LAN.
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
preSetup =
|
||||
let
|
||||
gen-key = config.sane.fs."/run/wg-home.priv".unit;
|
||||
in
|
||||
"${pkgs.systemd}/bin/systemctl start '${gen-key}'";
|
||||
|
||||
ips = [
|
||||
"${cfg.ip}/24"
|
||||
];
|
||||
|
||||
peers =
|
||||
let
|
||||
all-peers = mapAttrsToList (_: hostcfg: hostcfg.wg-home) config.sane.hosts.by-name;
|
||||
peer-list = filter (p: p.ip != null && p.ip != cfg.ip && p.pubkey != null) all-peers;
|
||||
in
|
||||
if cfg.ip == server-cfg.ip then
|
||||
# if we're the server, then we maintain the entire client list
|
||||
mkClientPeers peer-list
|
||||
else
|
||||
# but if we're a client, we maintain a single peer -- the server -- which does the actual routing
|
||||
[ (mkServerPeer peer-list) ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,24 +5,9 @@
|
|||
org.uninsane = rec {
|
||||
root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOfdSmFkrVT6DhpgvFeQKm3Fh9VKZ9DbLYOPOJWYQ0E8";
|
||||
git.root = root;
|
||||
|
||||
local = {
|
||||
# machine aliases i specify on my lan; not actually asserted as DNS
|
||||
desko.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPU5GlsSfbaarMvDA20bxpSZGWviEzXGD8gtrIowc1pX";
|
||||
desko.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw9NoRaYrM6LbDd3aFBc4yyBlxGQn8HjeHd/dZ3CfHk";
|
||||
|
||||
lappy.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDpmFdNSVPRol5hkbbCivRhyeENzb9HVyf9KutGLP2Zu";
|
||||
lappy.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILSJnqmVl9/SYQ0btvGb0REwwWY8wkdkGXQZfn/1geEc";
|
||||
|
||||
moby.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICrR+gePnl0nV/vy7I5BzrGeyVL+9eOuXHU1yNE3uCwU";
|
||||
moby.root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO1N/IT3nQYUD+dBlU1sTEEVMxfOyMkrrDeyHcYgnJvw";
|
||||
|
||||
servo.colin = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPS1qFzKurAdB9blkWomq8gI1g0T3sTs9LsmFOj5VtqX";
|
||||
servo.root = root;
|
||||
};
|
||||
};
|
||||
|
||||
com.github = rec {
|
||||
com.github = {
|
||||
# documented here: <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints>
|
||||
# Github actually uses multiple keys -- one per format
|
||||
root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
|
||||
|
|
|
@ -57,7 +57,7 @@ in
|
|||
options = {
|
||||
sane.ssh.pubkeys = mkOption {
|
||||
type = types.attrsOf coercedToKey;
|
||||
default = [];
|
||||
default = {};
|
||||
description = ''
|
||||
mapping from "user@host" to pubkey.
|
||||
'';
|
||||
|
|
|
@ -60,8 +60,8 @@ sops:
|
|||
cWplOHBNWjlJdGI3ZWtJc0t4Mk9URG8KE+9IPGYZsIs2PaDJ2AUE4gB4QEj5zo6P
|
||||
aZVbubu6Tbg+tD/98RkfWAkNvoVeDYuLNPDNgqOL0UgCQiTrPPaTjw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2022-12-15T09:12:44Z"
|
||||
mac: ENC[AES256_GCM,data:QQiTsQogs6MP9X0lrpf2FeSia6SeQP5/9dtUrWQOd2Vh/s0fBJfIGUdLeLgt5itvaD5QywY6lN9Rsx++BUN0rrwUu/uF42KOMC7wjHdSv07CYuDfvlFZItuIo5eWlfcEq9+p6/VwUXY0TU3M6Ex+mABT5XK67tnLuh/SoHUl+DA=,iv:12sa+wFdO5T7pZrLM3mnEwoJ0WmXZZLKpucEgMYQHMI=,tag:zZEz6+vTma6KDMwXi/fNZA==,type:str]
|
||||
lastmodified: "2023-01-20T07:08:51Z"
|
||||
mac: ENC[AES256_GCM,data:EqVTjLmDLjAxMWvJKIqDjpUKj7oVArFSQzygVZVCYHQTp0v+EGxpn1cP8x+6frQHx8EZts090iqCpcSicwGefAe6VBVvdf408YgB9+qm6QawUMaqDypoM1q4RdOjJEKxyZ408RYorQP6OA/3yctOk39P8RbMUUBlnLvqn38mFeY=,iv:wpDcFZKRGhhoHLno1IvppCz4HUAZb4atX1mUvYZbjg4=,tag:X4BdSfGE1kWFjs4T8YZR0A==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.7.3
|
||||
|
|
|
@ -9,7 +9,6 @@ wg_ovpnd_us_privkey: ENC[AES256_GCM,data:5YkQ4r7HNWiRr/5pa1XfexxtJAz6kDjX+hNiZch
|
|||
wg_ovpnd_us-atl_privkey: ENC[AES256_GCM,data:NMguzx35VvOAo37U9pGD5bYa/ghWeSK5tVh2XRNsfKjMPhMa44lm3pTscdU=,iv:f9hBhMksL0VGT8k2RsztU9AjR2AIIL+Z2Ls24UOPeNA=,tag:C46xDGb2d32mmHWl7WQb2g==,type:str]
|
||||
wg_ovpnd_us-mi_privkey: ENC[AES256_GCM,data:uEC3UOOqn1l7KwGmOxKvXccPR9Gu8/BNTlpXxXlNWf19/pIX1CLPORUWme0=,iv:cnPGghGBAsIsR86F1hPZawLWlY/pLCNF/1cg6gjrIKE=,tag:LqMbpGklQH0GX7dNNV3/8Q==,type:str]
|
||||
wg_ovpnd_ukr_privkey: ENC[AES256_GCM,data:5zfhsZnBk0Kb9Nb/3igsV/fN0ZDjwTAGTKyMLMly/l7MlJe6MEmd5Lv+JT8=,iv:Mov9eUP8WfvzfZ6NljgLolJ49GSqR7eSV+k0dgE1+1I=,tag:O9UtGX2qt+qEvabcsA0vIA==,type:str]
|
||||
wg_home_privkey: ENC[AES256_GCM,data:c8wabBMlip3QlJ6P0ZMU/Y1Sp5V9NjVRB0sQGr9BGm1LFoSs9pkS+Su+SD8=,iv:hnIjd15g+zWqPnXu4puLrKSn2N4zVrXp32xnb315VQk=,tag:hFKSV6qb5ns9CvvuXBWLLg==,type:str]
|
||||
#ENC[AES256_GCM,data:qlF8rpSMUv6Z/YrOTp7WYs0lcpmSIi/r+gCuiw==,iv:cneNp/0av/ttQvnW4JVX9mj3261QFAzkLIzEMwiKwE8=,tag:FFsPUQBsSeImtymawY4eSg==,type:comment]
|
||||
router_passwd: ENC[AES256_GCM,data:Tya3Pd75Yu4=,iv:lqi7SavFnymL+uOQXDEzGxgikB6/ckNOBifjhyjXn1Q=,tag:HG3kf6e2g53uNUGI9FXyqQ==,type:str]
|
||||
jackett_apikey: ENC[AES256_GCM,data:2oGczau3f/w/5iCx3aft0V/t0tO5zsr5Xi/HQ1koTTo=,iv:33VPT8GYCPPJ2RUBP6yuLep9YX/VMW9Kt3MyQPmZuO0=,tag:TUIbutJKV5e3Kc9INk5VUA==,type:str]
|
||||
|
@ -91,8 +90,8 @@ sops:
|
|||
YmhsY0FaSW5oWVNJMlhUSDRCeWQ4KzAKaQp321XYtAZ98f4QMl5PxivAYm6VMF43
|
||||
wCThiQgvYAP59jvVDTZngvfWAD5PyWVVvMNbjHGvAzK5WnsTPmxlsg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2023-01-19T09:45:50Z"
|
||||
mac: ENC[AES256_GCM,data:v+gQu9PFcLXxatllrlMX67ZaIr4MIn7v0YuQEfw2ZnIgcxlukW/wInCf8aOt+172Yme1e/YVf4X8KePUNQlFwPdfw4S+NDUSC92Kuu3/ZD3tYk46VGNPGfhiz9APeCej6oBebo4RBrIhFq5HUJe5pm2W5So/YAldUnCmfgSNyBs=,iv:plMcyLC3fj7KpOBbYcSDmY7vZpV/klHre12jbTKUhVw=,tag:yiJR3HmVYApF9CXruGGq4g==,type:str]
|
||||
lastmodified: "2023-01-20T06:57:29Z"
|
||||
mac: ENC[AES256_GCM,data:J/yLlcmlX6st/d6c8eL/6DKZiHAELb0/zj+5qOjoE2uAgTTFnojaP4ssrmt7BaLQF1MQNnvkchvuwRv+dAVTXkuYPuDWS3YriAKQIXUx9sHIEoY6Aqa37eBwUNUBuxoR6FvfOGtXrIZuS0f7hZr+ddBZgCSBBE54yeH68Va1tZk=,iv:Y/T8qykrqRVQ8eMkNH2DZa6XoGd5nL18h/2SJucVAD8=,tag:OwZfOyLc29c1bJJIA9IW3Q==,type:str]
|
||||
pgp: []
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.7.3
|
||||
|
|
Loading…
Reference in New Issue
Block a user