Unverified Commit 0fe515d3 authored by Doron Behar's avatar Doron Behar Committed by GitHub
Browse files

nixos/syncthing: add guiPasswordFile option, add and move Syncthing tests (#446197)

parents b1ebcf1a dffad9df
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -236,13 +236,14 @@ let
    +
      /*
        Now we update the other settings defined in cleanedConfig which are not
        "folders" or "devices".
        "folders", "devices", or "guiPasswordFile".
      */
      (lib.pipe cleanedConfig [
        builtins.attrNames
        (lib.subtractLists [
          "folders"
          "devices"
          "guiPasswordFile"
        ])
        (map (subOption: ''
          curl -X PUT -d ${
@@ -251,6 +252,12 @@ let
        ''))
        (lib.concatStringsSep "\n")
      ])
    +
      # Now we hash the contents of guiPasswordFile and use the result to update the gui password
      (lib.optionalString (cfg.guiPasswordFile != null) ''
        ${pkgs.mkpasswd}/bin/mkpasswd -m bcrypt --stdin <"${cfg.guiPasswordFile}" | tr -d "\n" > "$RUNTIME_DIRECTORY/password_bcrypt"
        curl -X PATCH --variable "pw_bcrypt@$RUNTIME_DIRECTORY/password_bcrypt" --expand-json '{ "password": "{{pw_bcrypt}}" }' ${curlAddressArgs "/rest/config/gui"}
      '')
    + ''
      # restart Syncthing if required
      if curl ${curlAddressArgs "/rest/config/restart-required"} |
@@ -285,6 +292,14 @@ in
        '';
      };

      guiPasswordFile = mkOption {
        type = types.nullOr types.str;
        default = null;
        description = ''
          Path to file containing the plaintext password for Syncthing's GUI.
        '';
      };

      overrideDevices = mkOption {
        type = types.bool;
        default = true;
@@ -837,6 +852,12 @@ in
          from the configuration, creating path conflicts.
        '';
      }
      {
        assertion = (lib.hasAttrByPath [ "gui" "password" ] cfg.settings) -> cfg.guiPasswordFile == null;
        message = ''
          Please use only one of services.syncthing.settings.gui.password or services.syncthing.guiPasswordFile.
        '';
      }
    ];

    networking.firewall = mkIf cfg.openDefaultPorts {
+10 −6
Original line number Diff line number Diff line
@@ -1402,12 +1402,16 @@ in
  switchTest = runTest ./switch-test.nix;
  sx = runTest ./sx.nix;
  sympa = runTest ./sympa.nix;
  syncthing = runTest ./syncthing.nix;
  syncthing-folders = runTest ./syncthing-folders.nix;
  syncthing-init = runTest ./syncthing-init.nix;
  syncthing-many-devices = runTest ./syncthing-many-devices.nix;
  syncthing-no-settings = runTest ./syncthing-no-settings.nix;
  syncthing-relay = runTest ./syncthing-relay.nix;
  syncthing = runTest ./syncthing/main.nix;
  syncthing-folders = runTest ./syncthing/folders.nix;
  syncthing-guiPassword = runTest ./syncthing/guiPassword.nix;
  syncthing-guiPasswordFile = runTest ./syncthing/guiPasswordFile.nix;
  syncthing-init = runTest ./syncthing/init.nix;
  # FIXME: Test has been failing since 2025-07-06:
  # https://github.com/NixOS/nixpkgs/issues/447674
  # syncthing-many-devices = runTest ./syncthing/many-devices.nix;
  syncthing-no-settings = runTest ./syncthing/no-settings.nix;
  syncthing-relay = runTest ./syncthing/relay.nix;
  sysfs = runTest ./sysfs.nix;
  sysinit-reactivation = runTest ./sysinit-reactivation.nix;
  systemd = runTest ./systemd.nix;
+56 −0
Original line number Diff line number Diff line
{ lib, pkgs, ... }:
{
  name = "syncthing-guiPassword";
  meta.maintainers = with lib.maintainers; [ nullcube ];
  enableOCR = true;

  nodes.machine = {
    imports = [ ../common/x11.nix ];
    environment.systemPackages = with pkgs; [
      syncthing
      xdotool
    ];

    programs.firefox = {
      enable = true;
      preferences = {
        # Prevent firefox from asking to save the password
        "signon.rememberSignons" = false;
      };
    };

    services.syncthing = {
      enable = true;
      settings.options.urAccepted = -1;
      settings.gui = {
        insecureAdminAccess = false;
        user = "alice";
        password = "alice_password";
      };
    };
  };

  testScript = ''
    machine.wait_for_unit("syncthing.service")
    machine.wait_for_x()
    machine.execute("xterm -e 'firefox 127.0.0.1:8384' >&2 &")
    machine.wait_for_window("Syncthing")
    machine.screenshot("pre-login")

    with subtest("Syncthing requests authentication"):
      machine.wait_for_text("Authentication Required", 10)

    with subtest("Syncthing password is valid"):
      machine.execute("xdotool type \"alice\"")
      machine.execute("xdotool key Tab")
      machine.execute("xdotool type \"alice_password\"")
      machine.execute("xdotool key Enter")
      machine.sleep(2)
      machine.wait_for_text("This Device", 10)
      machine.screenshot("post-login")

    with subtest("Plaintext Syncthing password is not in final config"):
      config = machine.succeed("cat /var/lib/syncthing/.config/syncthing/config.xml")
      assert "alice_password" not in config
  '';
}
+56 −0
Original line number Diff line number Diff line
{ lib, pkgs, ... }:
{
  name = "syncthing-guiPasswordFile";
  meta.maintainers = with lib.maintainers; [ nullcube ];
  enableOCR = true;

  nodes.machine = {
    imports = [ ../common/x11.nix ];
    environment.systemPackages = with pkgs; [
      syncthing
      xdotool
    ];

    programs.firefox = {
      enable = true;
      preferences = {
        # Prevent firefox from asking to save the password
        "signon.rememberSignons" = false;
      };
    };

    services.syncthing = {
      enable = true;
      settings.options.urAccepted = -1;
      settings.gui = {
        insecureAdminAccess = false;
        user = "alice";
      };
      guiPasswordFile = (pkgs.writeText "syncthing-password-file" ''alice_password'').outPath;
    };
  };

  testScript = ''
    machine.wait_for_unit("syncthing.service")
    machine.wait_for_x()
    machine.execute("xterm -e 'firefox 127.0.0.1:8384' >&2 &")
    machine.wait_for_window("Syncthing")
    machine.screenshot("pre-login")

    with subtest("Syncthing requests authentication"):
      machine.wait_for_text("Authentication Required", 10)

    with subtest("Syncthing password is valid"):
      machine.execute("xdotool type \"alice\"")
      machine.execute("xdotool key Tab")
      machine.execute("xdotool type \"alice_password\"")
      machine.execute("xdotool key Enter")
      machine.sleep(2)
      machine.wait_for_text("This Device", 10)
      machine.screenshot("post-login")

    with subtest("Plaintext Syncthing password is not in final config"):
      config = machine.succeed("cat /var/lib/syncthing/.config/syncthing/config.xml")
      assert "alice_password" not in config
  '';
}
Loading