generate.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright (C) 2021 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // Package generate implements the `syncthing generate` subcommand.
  7. package generate
  8. import (
  9. "bufio"
  10. "context"
  11. "crypto/tls"
  12. "fmt"
  13. "os"
  14. "github.com/syncthing/syncthing/lib/config"
  15. "github.com/syncthing/syncthing/lib/events"
  16. "github.com/syncthing/syncthing/lib/fs"
  17. "github.com/syncthing/syncthing/lib/locations"
  18. "github.com/syncthing/syncthing/lib/logger"
  19. "github.com/syncthing/syncthing/lib/protocol"
  20. "github.com/syncthing/syncthing/lib/syncthing"
  21. )
  22. type CLI struct {
  23. GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"`
  24. GUIPassword string `placeholder:"STRING" help:"Specify new GUI authentication password (use - to read from standard input)"`
  25. NoDefaultFolder bool `help:"Don't create the \"default\" folder on first startup" env:"STNODEFAULTFOLDER"`
  26. NoPortProbing bool `help:"Don't try to find free ports for GUI and listen addresses on first startup" env:"STNOPORTPROBING"`
  27. }
  28. func (c *CLI) Run(l logger.Logger) error {
  29. // Support reading the password from a pipe or similar
  30. if c.GUIPassword == "-" {
  31. reader := bufio.NewReader(os.Stdin)
  32. password, _, err := reader.ReadLine()
  33. if err != nil {
  34. return fmt.Errorf("failed reading GUI password: %w", err)
  35. }
  36. c.GUIPassword = string(password)
  37. }
  38. if err := Generate(l, locations.GetBaseDir(locations.ConfigBaseDir), c.GUIUser, c.GUIPassword, c.NoDefaultFolder, c.NoPortProbing); err != nil {
  39. return fmt.Errorf("failed to generate config and keys: %w", err)
  40. }
  41. return nil
  42. }
  43. func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFolder, skipPortProbing bool) error {
  44. dir, err := fs.ExpandTilde(confDir)
  45. if err != nil {
  46. return err
  47. }
  48. if err := syncthing.EnsureDir(dir, 0o700); err != nil {
  49. return err
  50. }
  51. locations.SetBaseDir(locations.ConfigBaseDir, dir)
  52. var myID protocol.DeviceID
  53. certFile, keyFile := locations.Get(locations.CertFile), locations.Get(locations.KeyFile)
  54. cert, err := tls.LoadX509KeyPair(certFile, keyFile)
  55. if err == nil {
  56. l.Warnln("Key exists; will not overwrite.")
  57. } else {
  58. cert, err = syncthing.GenerateCertificate(certFile, keyFile)
  59. if err != nil {
  60. return fmt.Errorf("create certificate: %w", err)
  61. }
  62. }
  63. myID = protocol.NewDeviceID(cert.Certificate[0])
  64. l.Infoln("Device ID:", myID)
  65. cfgFile := locations.Get(locations.ConfigFile)
  66. cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
  67. if fs.IsNotExist(err) {
  68. if cfg, err = syncthing.DefaultConfig(cfgFile, myID, events.NoopLogger, noDefaultFolder, skipPortProbing); err != nil {
  69. return fmt.Errorf("create config: %w", err)
  70. }
  71. } else if err != nil {
  72. return fmt.Errorf("load config: %w", err)
  73. }
  74. ctx, cancel := context.WithCancel(context.Background())
  75. go cfg.Serve(ctx)
  76. defer cancel()
  77. var updateErr error
  78. waiter, err := cfg.Modify(func(cfg *config.Configuration) {
  79. updateErr = updateGUIAuthentication(l, &cfg.GUI, guiUser, guiPassword)
  80. })
  81. if err != nil {
  82. return fmt.Errorf("modify config: %w", err)
  83. }
  84. waiter.Wait()
  85. if updateErr != nil {
  86. return updateErr
  87. }
  88. if err := cfg.Save(); err != nil {
  89. return fmt.Errorf("save config: %w", err)
  90. }
  91. return nil
  92. }
  93. func updateGUIAuthentication(l logger.Logger, guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
  94. if guiUser != "" && guiCfg.User != guiUser {
  95. guiCfg.User = guiUser
  96. l.Infoln("Updated GUI authentication user name:", guiUser)
  97. }
  98. if guiPassword != "" && guiCfg.Password != guiPassword {
  99. if err := guiCfg.SetPassword(guiPassword); err != nil {
  100. return fmt.Errorf("failed to set GUI authentication password: %w", err)
  101. }
  102. l.Infoln("Updated GUI authentication password.")
  103. }
  104. return nil
  105. }