syncthing setup
This commit is contained in:
parent
24df8a251b
commit
52c074f973
19 changed files with 244 additions and 16 deletions
44
bin/gen-syncthing-cert
Executable file
44
bin/gen-syncthing-cert
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
set -euo pipefail
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf -- "$tmp"' EXIT
|
||||
|
||||
FILEPATH="${MANAGED_CLIENTS:-./var/syncthing-managed-clients.json}"
|
||||
PKI_PATH="${PKI_PATH:-./pki/syncthing}"
|
||||
SECRETS_PATH="${SECRETS_PATH:-secrets/syncthing}"
|
||||
|
||||
first_missing=$(
|
||||
jq -r '
|
||||
. as $root
|
||||
| $root.managed_clients[]
|
||||
| select($root.hashes[.] | not)
|
||||
' $FILEPATH \
|
||||
| head -n 1 \
|
||||
)
|
||||
[ -z "$first_missing" ] && echo "Done" >&2 && exit 0
|
||||
|
||||
echo "Generating cerificate for $first_missing"
|
||||
mkdir $tmp/$first_missing
|
||||
hash=$(
|
||||
syncthing generate \
|
||||
--config $tmp/$first_missing \
|
||||
--data $tmp/$first_missing/data \
|
||||
| grep -oP '(?<=device=)[A-Z0-9-]+' \
|
||||
)
|
||||
|
||||
mkdir -p $PKI_PATH
|
||||
mv $tmp/$first_missing/cert.pem $PKI_PATH/$first_missing.cert
|
||||
|
||||
# Remove the file so agenix does not try to decrypt
|
||||
[ -f "$SECRETS_PATH/$first_missing.age" ] && rm "$SECRETS_PATH/$first_missing.age"
|
||||
agenix -e $SECRETS_PATH/$first_missing.age < $tmp/$first_missing/key.pem
|
||||
|
||||
jq --arg client "$first_missing" \
|
||||
--arg hash "$hash" \
|
||||
'.hashes[$client] = $hash' "$FILEPATH" \
|
||||
> "$tmp/new-syncthing-managed-clients.json" \
|
||||
&& mv "$tmp/new-syncthing-managed-clients.json" "$FILEPATH"
|
||||
|
||||
# Revoke self to handle next client
|
||||
"$0"
|
||||
|
|
@ -137,6 +137,8 @@
|
|||
colmena.packages.${system}.colmena
|
||||
agenix.packages.${system}.default
|
||||
pkgs.openssl
|
||||
pkgs.jq
|
||||
pkgs.syncthing
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,11 +16,12 @@
|
|||
];
|
||||
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
inputs.disko.nixosModules.disko
|
||||
./disko.nix
|
||||
inputs.nixos-hardware.nixosModules.framework-amd-ai-300-series
|
||||
inputs.lanzaboote.nixosModules.lanzaboote
|
||||
./disko.nix
|
||||
./hardware-configuration.nix
|
||||
./syncthing.nix
|
||||
];
|
||||
|
||||
# https://github.com/NixOS/nixos-hardware/issues/1603
|
||||
|
|
|
|||
8
host/fw/syncthing.nix
Normal file
8
host/fw/syncthing.nix
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{ ... }:
|
||||
{
|
||||
services.syncthing = {
|
||||
enable = true;
|
||||
user = "hd";
|
||||
configDir = "/home/hd/.config/syncthing";
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,19 @@
|
|||
{ ... }:
|
||||
let
|
||||
guiAddress = "127.0.0.1:8384";
|
||||
in
|
||||
{
|
||||
services.syncthing = {
|
||||
enable = false; # TODO
|
||||
enable = true;
|
||||
inherit guiAddress;
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
privateVirtualHosts."syncthing.roam.lan" = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://${guiAddress}/";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ with lib;
|
|||
extraGroups = [ "wheel" ];
|
||||
shell = pkgs.fish;
|
||||
packages = [ ];
|
||||
openssh.authorizedKeys.keys = var.ssh-keys.trusted;
|
||||
openssh.authorizedKeys.keys = var.ssh-keys.trusted-hd;
|
||||
hashedPasswordFile = config.age.secrets.hd-password.path;
|
||||
};
|
||||
users.root = {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@
|
|||
./common
|
||||
./desktop
|
||||
./nginx.nix
|
||||
./syncthing.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
65
mod/syncthing.nix
Normal file
65
mod/syncthing.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
var,
|
||||
config,
|
||||
lib,
|
||||
secrets,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.syncthing;
|
||||
this = config.networking.hostName;
|
||||
|
||||
is-managed = builtins.elem this var.syncthing-managed-clients.managed_clients;
|
||||
is-server = this == "roam";
|
||||
devices = lib.attrNames var.syncthing;
|
||||
devices-without-this = lib.remove this devices;
|
||||
type-encrypt = if is-server then "receiveencrypted" else "sendreceive";
|
||||
devices-encrypt =
|
||||
if is-server then
|
||||
devices-without-this
|
||||
else
|
||||
lib.remove "roam" devices-without-this
|
||||
++ [
|
||||
{
|
||||
name = "roam";
|
||||
encryptionPasswordFile = config.age.secrets.syncthing-password.path;
|
||||
}
|
||||
];
|
||||
|
||||
folders = {
|
||||
documents = {
|
||||
id = "documents-hd";
|
||||
path = if is-server then "/data/sync/documents-hd" else "/home/hd/Documents";
|
||||
type = type-encrypt;
|
||||
devices = devices-encrypt;
|
||||
versioning = {
|
||||
type = "simple";
|
||||
params.keep = "10";
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
age.secrets.syncthing-password = lib.mkIf (cfg.enable && !is-server) {
|
||||
file = secrets."syncthing-password.age";
|
||||
mode = "440";
|
||||
owner = config.services.syncthing.user;
|
||||
group = config.services.syncthing.group;
|
||||
};
|
||||
|
||||
age.secrets.syncthing-key = lib.mkIf (cfg.enable && is-managed) {
|
||||
file = secrets.syncthing."${this}.age";
|
||||
mode = "440";
|
||||
owner = config.services.syncthing.user;
|
||||
group = config.services.syncthing.group;
|
||||
};
|
||||
|
||||
services.syncthing = lib.mkIf cfg.enable {
|
||||
inherit folders;
|
||||
settings = {
|
||||
devices = var.syncthing;
|
||||
};
|
||||
key = lib.optionalAttrs is-managed config.age.secrets.syncthing-key.path;
|
||||
cert = lib.optionalAttrs is-managed "${../pki/syncthing + "/${this}.cert"}";
|
||||
};
|
||||
}
|
||||
11
pki/syncthing/fw.cert
Normal file
11
pki/syncthing/fw.cert
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBnzCCAVGgAwIBAgIIDgM/cFGbbhowBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
|
||||
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
|
||||
CXN5bmN0aGluZzAeFw0yNTEyMzAwMDAwMDBaFw00NTEyMjUwMDAwMDBaMEoxEjAQ
|
||||
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
|
||||
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhAGUklM+K4ns56bvytz1O
|
||||
dTs+XvCn2QQ+MbWh7z1ejy0Jo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
|
||||
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
|
||||
c3luY3RoaW5nMAUGAytlcANBAPmfwk4OUbL+S6TgCF2XqND4KeMO1LxXZ57IuqxC
|
||||
NK0w/nLcfebOoRlxCXLnW/VIKnhysr92nvW3SOuRqJOdDAg=
|
||||
-----END CERTIFICATE-----
|
||||
11
pki/syncthing/roam.cert
Normal file
11
pki/syncthing/roam.cert
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBnzCCAVGgAwIBAgIIP1QH/ukEmpEwBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0
|
||||
aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT
|
||||
CXN5bmN0aGluZzAeFw0yNTEyMzAwMDAwMDBaFw00NTEyMjUwMDAwMDBaMEoxEjAQ
|
||||
BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0
|
||||
ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhAI8QrJk1duZ5hrAhWh/l
|
||||
iaR/0kmK0H+TM+mtpt4YcOMMo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw
|
||||
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ
|
||||
c3luY3RoaW5nMAUGAytlcANBAP0aSI/TMmhS7RX5sRHlgCHRae4+4H5EDXckecGg
|
||||
uUA87D5Hq2sVULwhPWDbMChA3dbhLVN09mJO8IU8loQ+0Qs=
|
||||
-----END CERTIFICATE-----
|
||||
21
secrets.nix
21
secrets.nix
|
|
@ -1,12 +1,29 @@
|
|||
let
|
||||
pkgs = import <nixpkgs> { };
|
||||
inherit (pkgs) lib;
|
||||
keys = (import ./var { inherit lib; }).ssh-keys.root;
|
||||
ssh-keys = (import ./var { inherit lib; }).ssh-keys;
|
||||
keys = ssh-keys.root;
|
||||
trusted-keys = ssh-keys.trusted-root;
|
||||
secrets = [
|
||||
"roam/rclone-conf"
|
||||
"roam/firefox-sync-secret"
|
||||
"hd-password"
|
||||
"tlskey"
|
||||
];
|
||||
trusted-secrets = [
|
||||
# Can only be decrypted by clients
|
||||
"syncthing-password"
|
||||
];
|
||||
mkSecrets =
|
||||
keys: secrets: lib.mergeAttrsList (map (x: { "secrets/${x}.age".publicKeys = keys; }) secrets);
|
||||
syncthingManagedClients = (lib.importJSON ./var/syncthing-managed-clients.json).managed_clients;
|
||||
mkSyncthingSecret = client: {
|
||||
"secrets/syncthing/${client}.age".publicKeys = [ ssh-keys.by-host.root.${client} ];
|
||||
};
|
||||
syncthingSercrets = lib.mergeAttrsList (map mkSyncthingSecret syncthingManagedClients);
|
||||
in
|
||||
builtins.foldl' (acc: x: acc // { "secrets/${x}.age".publicKeys = keys; }) { } secrets
|
||||
lib.mergeAttrsList ([
|
||||
(mkSecrets keys secrets)
|
||||
(mkSecrets trusted-keys trusted-secrets)
|
||||
(syncthingSercrets)
|
||||
])
|
||||
|
|
|
|||
10
secrets/syncthing-password.age
Normal file
10
secrets/syncthing-password.age
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 FTMbvw XgjPSuj97t3rQ4LUyKQUFrcFF6cwQMSJFjTJDAG/Nj0
|
||||
ygJ4dT50p4F92ZOEBOA9gZbQ6DoSFLF7ES1nNA+5EkI
|
||||
-> ssh-ed25519 ydxpSQ 32hIAyI93zJvZ9W/RwzLAMM+HuICDXiu6nkwZYryl0Y
|
||||
O2gITm4v/e40dVjE5KAeea+j4VwdYVnxXSTbKVmc1ag
|
||||
-> ssh-ed25519 IbE9zA Uvjx9kbxOt6zIVZsIiLValtgKeL7hufv8O45OrU7Nww
|
||||
yAzSC6ggAInYOfbh9DmcnoAbrun8LLTWpqF9YK9dzCw
|
||||
--- oVhNPf8TcFS/eZpcR0Yae3jddZziNWXPweId4HledhM
|
||||
„кÖ—¦
{
ät •q[ö<>;qDª€ðr£LA}ªÚú‚ܬ~}œÙKD_zgýv(¡d¢
|
||||
âUÝì …<ð_òôåÄ
¹°¥Õ°§ðÞ<14>‘Õ¯Ú婞zœIúiä,²
|
||||
BIN
secrets/syncthing/fw.age
Normal file
BIN
secrets/syncthing/fw.age
Normal file
Binary file not shown.
6
secrets/syncthing/roam.age
Normal file
6
secrets/syncthing/roam.age
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
age-encryption.org/v1
|
||||
-> ssh-ed25519 gbs8eg hQNgGRxHRPhSq5iWURBpPd3nZDh4ofjOtGlC9V3jnWc
|
||||
AJ8CDyayy0kUoPiUp0LMyHRBeKjRZUTD9BbQYPKuw90
|
||||
--- nxoh+y+XGoihcSXsc9CW5/KzuDCQkbpjiiXMnfJVs3A
|
||||
á\vU¯:I~ù¯§t/u§ßדÍ<E2809C>YU<>w$7š8[”xƒ]
<0A>¸;nÜRëG9Ð<á â ·úûþ×o,>áS–Ù¥Ë7Í¥aàB9láð{‚ˆ;;Úkä<6B>e7gjÃ<6A>¡$€¦
|
||||
e<EFBFBD>@8Èü³TÕÒñ;Ç€Ûã…ÌFužrLq¯váòËDîš8z"òªÐìãŽPT6nˆ<]<5D>
|
||||
|
|
@ -1,13 +1,20 @@
|
|||
{ ... }@inputs:
|
||||
{
|
||||
lib ? null,
|
||||
}:
|
||||
let
|
||||
inputs' = inputs // {
|
||||
lib' = if builtins.isNull lib then (import <nixpkgs> { }).lib else lib;
|
||||
inputs' = {
|
||||
lib = lib';
|
||||
var = outputs;
|
||||
};
|
||||
load-var = x: import x inputs';
|
||||
# watch out for cycles
|
||||
outputs = {
|
||||
"lan-dns" = import ./lan-dns.nix inputs';
|
||||
"ssh-keys" = import ./ssh-keys.nix inputs';
|
||||
"wg" = import ./wg.nix inputs';
|
||||
"lan-dns" = load-var ./lan-dns.nix;
|
||||
"ssh-keys" = load-var ./ssh-keys.nix;
|
||||
"wg" = load-var ./wg.nix;
|
||||
"syncthing" = load-var ./syncthing.nix;
|
||||
"syncthing-managed-clients" = lib'.importJSON ./syncthing-managed-clients.json;
|
||||
};
|
||||
in
|
||||
outputs
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ let
|
|||
}) var.wg.ips;
|
||||
custom-hosts = with var.wg.ips; {
|
||||
"git.lan" = roam;
|
||||
"syncthing.roam.lan" = roam;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
|
|
|
|||
|
|
@ -16,12 +16,15 @@ let
|
|||
};
|
||||
};
|
||||
keys' = mkKeys keys;
|
||||
mkTrusted =
|
||||
user: with keys'.by-host.${user}; [
|
||||
solo
|
||||
c2
|
||||
fw
|
||||
];
|
||||
in
|
||||
keys'
|
||||
// {
|
||||
trusted = with keys'.by-host.hd; [
|
||||
solo
|
||||
c2
|
||||
fw
|
||||
];
|
||||
trusted-hd = mkTrusted "hd";
|
||||
trusted-root = mkTrusted "root";
|
||||
}
|
||||
|
|
|
|||
10
var/syncthing-managed-clients.json
Normal file
10
var/syncthing-managed-clients.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"managed_clients": [
|
||||
"fw",
|
||||
"roam"
|
||||
],
|
||||
"hashes": {
|
||||
"fw": "YZGGXOT-MPFD7O4-ACLGOGT-LIMZVD3-7JBSZZR-LFCFWQL-BLO435I-LLH6GAL",
|
||||
"roam": "HMB7ZRF-OODFHHW-2QCIFFJ-M7COVK5-YUB3GKT-SI56D2U-CPTTJEP-R3ZKOQ7"
|
||||
}
|
||||
}
|
||||
18
var/syncthing.nix
Normal file
18
var/syncthing.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{ var, lib, ... }:
|
||||
let
|
||||
inherit (var.syncthing-managed-clients) managed_clients hashes;
|
||||
unmanaged = {
|
||||
# "roam".id = "OIKOKOT-LY4JWPX-T7OXE4D-I4ZC3IR-ZLMKFCO-IXSVEYZ-Y3FZOUB-LIG2XAO";
|
||||
};
|
||||
in
|
||||
assert (
|
||||
lib.assertMsg (
|
||||
builtins.attrNames hashes == managed_clients
|
||||
) "Not all declaratively configured syncthing clients have keys. Rerun ./bin/gen-syncthing-cert"
|
||||
);
|
||||
assert (
|
||||
lib.assertMsg (
|
||||
[ ] == (lib.intersectLists managed_clients (builtins.attrNames unmanaged))
|
||||
) "Syncthing clients must either be unmanaged or declaratively configured."
|
||||
);
|
||||
unmanaged // builtins.mapAttrs (_: v: { id = v; }) hashes
|
||||
Loading…
Add table
Add a link
Reference in a new issue