From 5eba758e82f0394e2305382feb4be07deda1683f Mon Sep 17 00:00:00 2001 From: SebastianStork Date: Sat, 7 Mar 2026 18:02:59 +0100 Subject: [PATCH] tests/syncthing: Init test --- modules/nixos/services/syncthing.nix | 56 +++++--- tests/syncthing/default.nix | 134 ++++++++++++++++++++ tests/syncthing/keys/nebula/ca.crt | 5 + tests/syncthing/keys/nebula/ca.key | 4 + tests/syncthing/keys/nebula/client1.crt | 6 + tests/syncthing/keys/nebula/client1.key | 3 + tests/syncthing/keys/nebula/client2.crt | 6 + tests/syncthing/keys/nebula/client2.key | 3 + tests/syncthing/keys/nebula/server.crt | 6 + tests/syncthing/keys/nebula/server.key | 3 + tests/syncthing/keys/syncthing/client1.cert | 11 ++ tests/syncthing/keys/syncthing/client1.id | 1 + tests/syncthing/keys/syncthing/client1.key | 3 + tests/syncthing/keys/syncthing/client2.cert | 11 ++ tests/syncthing/keys/syncthing/client2.id | 1 + tests/syncthing/keys/syncthing/client2.key | 3 + tests/syncthing/keys/syncthing/server.cert | 11 ++ tests/syncthing/keys/syncthing/server.id | 1 + tests/syncthing/keys/syncthing/server.key | 3 + 19 files changed, 254 insertions(+), 17 deletions(-) create mode 100644 tests/syncthing/default.nix create mode 100644 tests/syncthing/keys/nebula/ca.crt create mode 100644 tests/syncthing/keys/nebula/ca.key create mode 100644 tests/syncthing/keys/nebula/client1.crt create mode 100644 tests/syncthing/keys/nebula/client1.key create mode 100644 tests/syncthing/keys/nebula/client2.crt create mode 100644 tests/syncthing/keys/nebula/client2.key create mode 100644 tests/syncthing/keys/nebula/server.crt create mode 100644 tests/syncthing/keys/nebula/server.key create mode 100644 tests/syncthing/keys/syncthing/client1.cert create mode 100644 tests/syncthing/keys/syncthing/client1.id create mode 100644 tests/syncthing/keys/syncthing/client1.key create mode 100644 tests/syncthing/keys/syncthing/client2.cert create mode 100644 tests/syncthing/keys/syncthing/client2.id create mode 100644 tests/syncthing/keys/syncthing/client2.key create mode 100644 tests/syncthing/keys/syncthing/server.cert create mode 100644 tests/syncthing/keys/syncthing/server.id create mode 100644 tests/syncthing/keys/syncthing/server.key diff --git a/modules/nixos/services/syncthing.nix b/modules/nixos/services/syncthing.nix index 54e3679..9427643 100644 --- a/modules/nixos/services/syncthing.nix +++ b/modules/nixos/services/syncthing.nix @@ -10,8 +10,6 @@ let netCfg = config.custom.networking; inherit (config.services.syncthing) dataDir; - - useSopsSecrets = config.custom.sops.secretsData |> lib.hasAttr "syncthing"; in { options.custom.services.syncthing = { @@ -47,31 +45,47 @@ in "Videos" ]; }; + + certFile = lib.mkOption { + type = lib.types.nullOr self.lib.types.existingPath; + default = null; + }; + keyFile = lib.mkOption { + type = lib.types.nullOr self.lib.types.existingPath; + default = null; + }; }; config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = cfg.isServer -> (cfg.gui.domain != null); - message = "Syncthing requires `gui.domain` to be set when `isServer` is enabled"; - } - { - assertion = (cfg.gui.domain != null) -> (self.lib.isPrivateDomain cfg.gui.domain); - message = self.lib.mkUnprotectedMessage "Syncthing-GUI"; - } - ]; + assertions = lib.singleton { + assertion = (cfg.gui.domain != null) -> (self.lib.isPrivateDomain cfg.gui.domain); + message = self.lib.mkUnprotectedMessage "Syncthing-GUI"; + }; - sops.secrets = lib.mkIf useSopsSecrets { - "syncthing/cert" = { + sops.secrets = { + "syncthing/cert" = lib.mkIf (cfg.certFile == null) { owner = config.services.syncthing.user; restartUnits = [ "syncthing.service" ]; }; - "syncthing/key" = { + "syncthing/key" = lib.mkIf (cfg.keyFile == null) { owner = config.services.syncthing.user; restartUnits = [ "syncthing.service" ]; }; }; + environment.etc = { + "syncthing/cert.pem" = lib.mkIf (cfg.certFile != null) { + source = cfg.certFile; + mode = "0644"; + user = config.services.syncthing.user; + }; + "syncthing/key.pem" = lib.mkIf (cfg.keyFile != null) { + source = cfg.keyFile; + mode = "0600"; + user = config.services.syncthing.user; + }; + }; + services = { syncthing = { enable = true; @@ -82,8 +96,16 @@ in guiAddress = "localhost:${toString cfg.gui.port}"; - cert = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/cert".path; - key = lib.mkIf useSopsSecrets config.sops.secrets."syncthing/key".path; + cert = + if (cfg.certFile != null) then + "/etc/syncthing/cert.pem" + else + config.sops.secrets."syncthing/cert".path; + key = + if (cfg.keyFile != null) then + "/etc/syncthing/key.pem" + else + config.sops.secrets."syncthing/key".path; settings = let diff --git a/tests/syncthing/default.nix b/tests/syncthing/default.nix new file mode 100644 index 0000000..275932e --- /dev/null +++ b/tests/syncthing/default.nix @@ -0,0 +1,134 @@ +{ + inputs, + self, + lib, + ... +}: +{ + node.specialArgs = { inherit inputs self; }; + + defaults = + { nodes, config, ... }: + { + imports = [ self.nixosModules.default ]; + + _module.args.allHosts = nodes |> lib.mapAttrs (_: node: { config = node; }); + + users = { + mutableUsers = false; + users.seb = { + isNormalUser = true; + password = "seb"; + }; + }; + + custom = { + networking.underlay.interface = "eth1"; + + services = { + nebula = { + caCertificateFile = ./keys/nebula/ca.crt; + certificateFile = ./keys/nebula/${config.networking.hostName}.crt; + privateKeyFile = ./keys/nebula/${config.networking.hostName}.key; + }; + + syncthing = { + enable = true; + deviceId = ./keys/syncthing/${config.networking.hostName}.id |> lib.readFile |> lib.trim; + certFile = ./keys/syncthing/${config.networking.hostName}.cert; + keyFile = ./keys/syncthing/${config.networking.hostName}.key; + }; + }; + }; + }; + + nodes = { + server = { + custom = { + networking = { + overlay = { + address = "10.254.250.1"; + isLighthouse = true; + role = "server"; + }; + underlay = { + cidr = "192.168.0.1/16"; + isPublic = true; + }; + }; + + services.syncthing.isServer = true; + }; + }; + + client1 = { + custom.networking = { + overlay = { + address = "10.254.250.2"; + role = "client"; + }; + underlay.cidr = "192.168.0.2/16"; + }; + }; + + client2 = { + custom = { + networking = { + overlay = { + address = "10.254.250.3"; + role = "client"; + }; + underlay.cidr = "192.168.0.3/16"; + }; + + services.syncthing.folders = [ "Documents" ]; + }; + }; + }; + + testScript = + { nodes, ... }: + let + serverNetCfg = nodes.server.custom.networking.overlay; + client1NetCfg = nodes.client1.custom.networking.overlay; + client2NetCfg = nodes.client2.custom.networking.overlay; + + getSyncPort = hostName: nodes.${hostName}.custom.services.syncthing.syncPort |> toString; + in + '' + start_all() + + server.wait_for_unit("syncthing.service") + client1.wait_for_unit("syncthing.service") + client2.wait_for_unit("syncthing.service") + + server.wait_for_unit("syncthing-init.service") + client1.wait_for_unit("syncthing-init.service") + client2.wait_for_unit("syncthing-init.service") + + server.wait_for_open_port(${getSyncPort "server"}, "${serverNetCfg.address}") + client1.wait_for_open_port(${getSyncPort "client1"}, "${client1NetCfg.address}") + client2.wait_for_open_port(${getSyncPort "client2"}, "${client2NetCfg.address}") + + with subtest("Three way sync of Documents"): + server.wait_for_file("/var/lib/syncthing/Documents") + client1.wait_for_file("/home/seb/Documents") + client2.wait_for_file("/home/seb/Documents") + + server.succeed("sudo --user=syncthing touch /var/lib/syncthing/Documents/server") + client1.succeed("sudo --user=seb touch /home/seb/Documents/client1") + client2.succeed("sudo --user=seb touch /home/seb/Documents/client2") + + server.wait_for_file("/var/lib/syncthing/Documents/client1") + server.wait_for_file("/var/lib/syncthing/Documents/client2") + client1.wait_for_file("/home/seb/Documents/server") + client1.wait_for_file("/home/seb/Documents/client2") + client2.wait_for_file("/home/seb/Documents/server") + client2.wait_for_file("/home/seb/Documents/client1") + + with subtest("Two way sync of Pictures"): + server.wait_for_file("/var/lib/syncthing/Pictures") + client1.wait_for_file("/home/seb/Pictures") + client2.fail("test -d /home/seb/Pictures") + ''; +} diff --git a/tests/syncthing/keys/nebula/ca.crt b/tests/syncthing/keys/nebula/ca.crt new file mode 100644 index 0000000..5b6272b --- /dev/null +++ b/tests/syncthing/keys/nebula/ca.crt @@ -0,0 +1,5 @@ +-----BEGIN NEBULA CERTIFICATE V2----- +MHygFoAEdGVzdIQB/4UEaY8shIYFASWHSoSCIM0af4sq7VnPAySG5h9fwiq/XHvD +a0Ssbk1+KVWFpR71g0DaZP8qR35Zut2z9i9D2bCDuagQNvvxCrkZ3JcF0gMvWu3u +uzKQMKzJSqipppgL/n3iQwwsBAoHYrx1XAY6zXgE +-----END NEBULA CERTIFICATE V2----- diff --git a/tests/syncthing/keys/nebula/ca.key b/tests/syncthing/keys/nebula/ca.key new file mode 100644 index 0000000..4a22aa1 --- /dev/null +++ b/tests/syncthing/keys/nebula/ca.key @@ -0,0 +1,4 @@ +-----BEGIN NEBULA ED25519 PRIVATE KEY----- +8kwpb4GZIphJmamXx0ZrLm5TxPZ7G88L44mrdT2dQp3NGn+LKu1ZzwMkhuYfX8Iq +v1x7w2tErG5NfilVhaUe9Q== +-----END NEBULA ED25519 PRIVATE KEY----- diff --git a/tests/syncthing/keys/nebula/client1.crt b/tests/syncthing/keys/nebula/client1.crt new file mode 100644 index 0000000..e7fab0c --- /dev/null +++ b/tests/syncthing/keys/nebula/client1.crt @@ -0,0 +1,6 @@ +-----BEGIN NEBULA CERTIFICATE V2----- +MIG8oFaAB2NsaWVudDGhBwQFCv76AhijEwwGY2xpZW50DAlzeW5jdGhpbmeFBGmr +V+WGBQElh0qDhyA8ckeBMU2fPOMFe8cEQoAZW3a1/xd+hPuJgkRptJYkIIIg5cgC +6ZPW+oUy91XjQ+2fR37eeSh2Oh0mUeK3QGOf62uDQMur6GhOZatAfhbvYXYpI79Z +4lM7tUWXLH/DBIAMeqVj2iStj5tz8vDNo/ixG2igMYU7ZaDTw7gLgOoue859fQY= +-----END NEBULA CERTIFICATE V2----- diff --git a/tests/syncthing/keys/nebula/client1.key b/tests/syncthing/keys/nebula/client1.key new file mode 100644 index 0000000..1f79f7d --- /dev/null +++ b/tests/syncthing/keys/nebula/client1.key @@ -0,0 +1,3 @@ +-----BEGIN NEBULA X25519 PRIVATE KEY----- +bpb3z7RewB2Pcg9d+mkMVjoeRRortg+v+f0ljk6jscI= +-----END NEBULA X25519 PRIVATE KEY----- diff --git a/tests/syncthing/keys/nebula/client2.crt b/tests/syncthing/keys/nebula/client2.crt new file mode 100644 index 0000000..fa33993 --- /dev/null +++ b/tests/syncthing/keys/nebula/client2.crt @@ -0,0 +1,6 @@ +-----BEGIN NEBULA CERTIFICATE V2----- +MIG8oFaAB2NsaWVudDKhBwQFCv76AxijEwwGY2xpZW50DAlzeW5jdGhpbmeFBGmr +V++GBQElh0qDhyA8ckeBMU2fPOMFe8cEQoAZW3a1/xd+hPuJgkRptJYkIIIgVOR5 +Nq5iScqDyYvY9f5Hk3jjq7qfxHEUhdAaeCDTx0KDQNoes3D5SbgZNKLtsXpAc8PL +PahMWZoDaySnoLpPJIhRSsav6ZbfUdDGzwbvhW+D8u6sZOqP+HGQSxaPU6ap3As= +-----END NEBULA CERTIFICATE V2----- diff --git a/tests/syncthing/keys/nebula/client2.key b/tests/syncthing/keys/nebula/client2.key new file mode 100644 index 0000000..346177f --- /dev/null +++ b/tests/syncthing/keys/nebula/client2.key @@ -0,0 +1,3 @@ +-----BEGIN NEBULA X25519 PRIVATE KEY----- +Y4mF5DL8TW/Q+OSGNgh942LqlxuWQA2xntbyWLTQPF0= +-----END NEBULA X25519 PRIVATE KEY----- diff --git a/tests/syncthing/keys/nebula/server.crt b/tests/syncthing/keys/nebula/server.crt new file mode 100644 index 0000000..6d55371 --- /dev/null +++ b/tests/syncthing/keys/nebula/server.crt @@ -0,0 +1,6 @@ +-----BEGIN NEBULA CERTIFICATE V2----- +MIG7oFWABnNlcnZlcqEHBAUK/voBGKMTDAZzZXJ2ZXIMCXN5bmN0aGluZ4UEaaxC +dIYFASWHSoOHIDxyR4ExTZ884wV7xwRCgBlbdrX/F36E+4mCRGm0liQggiAPf3NY +OWszPbspqt89XZ1KoXShQaLFp+7snVtn4cLnCoNAPtMQGcr0qxFIEEK0K/a5cvk7 +Uc7AMLB0zjzUFlw881iI4dGLSSbKjs2c6RMtxmKHOwm9sqCc18QITQ822cwPDg== +-----END NEBULA CERTIFICATE V2----- diff --git a/tests/syncthing/keys/nebula/server.key b/tests/syncthing/keys/nebula/server.key new file mode 100644 index 0000000..3af64f2 --- /dev/null +++ b/tests/syncthing/keys/nebula/server.key @@ -0,0 +1,3 @@ +-----BEGIN NEBULA X25519 PRIVATE KEY----- +fx4GTdb3a1GUeRoSqFQH3Jb9KEqSipFoYKM/34D16fs= +-----END NEBULA X25519 PRIVATE KEY----- diff --git a/tests/syncthing/keys/syncthing/client1.cert b/tests/syncthing/keys/syncthing/client1.cert new file mode 100644 index 0000000..db46619 --- /dev/null +++ b/tests/syncthing/keys/syncthing/client1.cert @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnzCCAVGgAwIBAgIIZ8OMU1XkHLYwBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0 +aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT +CXN5bmN0aGluZzAeFw0yNjAzMDYwMDAwMDBaFw00NjAzMDEwMDAwMDBaMEoxEjAQ +BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0 +ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhADdeAXQaYGTf6XLeVx53 +BsAK3ODoIwb/IHobmgTHsSwLo1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw +FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ +c3luY3RoaW5nMAUGAytlcANBADyH0WLqPku8KqsnK5/PyVo6u7YiH961dQmOaDp0 +6GyMJogD8ieICsCIt+IeZnhc9CTiJ9++S1RfnF5+R8UVDAs= +-----END CERTIFICATE----- diff --git a/tests/syncthing/keys/syncthing/client1.id b/tests/syncthing/keys/syncthing/client1.id new file mode 100644 index 0000000..6159857 --- /dev/null +++ b/tests/syncthing/keys/syncthing/client1.id @@ -0,0 +1 @@ +IB7WP5L-6BQ2XJE-GQ6LE5A-ADBH4VM-G5KPGN5-JRYKLXP-XOGCVFM-TC7AXAB \ No newline at end of file diff --git a/tests/syncthing/keys/syncthing/client1.key b/tests/syncthing/keys/syncthing/client1.key new file mode 100644 index 0000000..319c714 --- /dev/null +++ b/tests/syncthing/keys/syncthing/client1.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIIE7hYkrJGXQ6gwZytxBaWvMnqs9kZB01WUrUiWZrXxc +-----END PRIVATE KEY----- diff --git a/tests/syncthing/keys/syncthing/client2.cert b/tests/syncthing/keys/syncthing/client2.cert new file mode 100644 index 0000000..018bb3a --- /dev/null +++ b/tests/syncthing/keys/syncthing/client2.cert @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnzCCAVGgAwIBAgIIYBqDIQR6+i0wBQYDK2VwMEoxEjAQBgNVBAoTCVN5bmN0 +aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQBgNVBAMT +CXN5bmN0aGluZzAeFw0yNjAzMDYwMDAwMDBaFw00NjAzMDEwMDAwMDBaMEoxEjAQ +BgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0 +ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzAqMAUGAytlcAMhAGI9m1y3be3GDH5n1O9o +FHrGcGQM9Uc6ws7DjzUM/wPho1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYw +FAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJ +c3luY3RoaW5nMAUGAytlcANBAD39uIYHfausQ7UHoGHp/xsEdYVeybdbfObIPagY +a5udetlOfER5EwGAaqGNSG6C+R+4NgxRU4SU0KMWn6AxDQc= +-----END CERTIFICATE----- diff --git a/tests/syncthing/keys/syncthing/client2.id b/tests/syncthing/keys/syncthing/client2.id new file mode 100644 index 0000000..5a24ef4 --- /dev/null +++ b/tests/syncthing/keys/syncthing/client2.id @@ -0,0 +1 @@ +CZAD2UP-QINQEBX-YO5HKF4-DWXOI2O-C3O7PNP-ATHP2ZE-Y35QSMQ-VM4E3Q6 \ No newline at end of file diff --git a/tests/syncthing/keys/syncthing/client2.key b/tests/syncthing/keys/syncthing/client2.key new file mode 100644 index 0000000..76d1316 --- /dev/null +++ b/tests/syncthing/keys/syncthing/client2.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIMGEoiShb3fDL4xKBGbcTD1pm0Q+zMhqcrQDBY7Clvn9 +-----END PRIVATE KEY----- diff --git a/tests/syncthing/keys/syncthing/server.cert b/tests/syncthing/keys/syncthing/server.cert new file mode 100644 index 0000000..805e0c5 --- /dev/null +++ b/tests/syncthing/keys/syncthing/server.cert @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBoDCCAVKgAwIBAgIJAPrpCfMh1qXtMAUGAytlcDBKMRIwEAYDVQQKEwlTeW5j +dGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJhdGVkMRIwEAYDVQQD +EwlzeW5jdGhpbmcwHhcNMjYwMzA2MDAwMDAwWhcNNDYwMzAxMDAwMDAwWjBKMRIw +EAYDVQQKEwlTeW5jdGhpbmcxIDAeBgNVBAsTF0F1dG9tYXRpY2FsbHkgR2VuZXJh +dGVkMRIwEAYDVQQDEwlzeW5jdGhpbmcwKjAFBgMrZXADIQCP17+WnSg6hU2fBqdm +53QMqhPE7uJhofVKPJdxu4xnGaNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW +MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC +CXN5bmN0aGluZzAFBgMrZXADQQBPdsirUqhXmor3UzuFkRMexbiJQQrHCq474DdX +gZyQgfGkxUEtlObNuoD7gVwB0Urn2P5nr9MeJIS//CG5be8F +-----END CERTIFICATE----- diff --git a/tests/syncthing/keys/syncthing/server.id b/tests/syncthing/keys/syncthing/server.id new file mode 100644 index 0000000..22f21f3 --- /dev/null +++ b/tests/syncthing/keys/syncthing/server.id @@ -0,0 +1 @@ +E74TB3J-GZ2F5ZG-J22G3B6-CJBSPRR-2C43YZJ-5PX47UT-OXJLWPA-EKWEGAB \ No newline at end of file diff --git a/tests/syncthing/keys/syncthing/server.key b/tests/syncthing/keys/syncthing/server.key new file mode 100644 index 0000000..47beb3c --- /dev/null +++ b/tests/syncthing/keys/syncthing/server.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIM0TySbarWhDA+d5N3UecLGiMCR3jsioPIMxCP5H02Bt +-----END PRIVATE KEY-----