telemetry.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Package telemetry provides telemetry information for SFTPGo, such as:
  2. // - health information (for health checks)
  3. // - metrics
  4. // - profiling information
  5. package telemetry
  6. import (
  7. "crypto/tls"
  8. "log"
  9. "net/http"
  10. "path/filepath"
  11. "runtime"
  12. "time"
  13. "github.com/go-chi/chi/v5"
  14. "github.com/drakkan/sftpgo/v2/common"
  15. "github.com/drakkan/sftpgo/v2/logger"
  16. sdklogger "github.com/drakkan/sftpgo/v2/sdk/logger"
  17. "github.com/drakkan/sftpgo/v2/util"
  18. )
  19. const (
  20. logSender = "telemetry"
  21. metricsPath = "/metrics"
  22. pprofBasePath = "/debug"
  23. )
  24. var (
  25. router *chi.Mux
  26. httpAuth common.HTTPAuthProvider
  27. certMgr *common.CertManager
  28. )
  29. // Conf telemetry server configuration.
  30. type Conf struct {
  31. // The port used for serving HTTP requests. 0 disable the HTTP server. Default: 10000
  32. BindPort int `json:"bind_port" mapstructure:"bind_port"`
  33. // The address to listen on. A blank value means listen on all available network interfaces. Default: "127.0.0.1"
  34. BindAddress string `json:"bind_address" mapstructure:"bind_address"`
  35. // Enable the built-in profiler.
  36. // The profiler will be accessible via HTTP/HTTPS using the base URL "/debug/pprof/"
  37. EnableProfiler bool `json:"enable_profiler" mapstructure:"enable_profiler"`
  38. // Path to a file used to store usernames and password for basic authentication.
  39. // This can be an absolute path or a path relative to the config dir.
  40. // We support HTTP basic authentication and the file format must conform to the one generated using the Apache
  41. // htpasswd tool. The supported password formats are bcrypt ($2y$ prefix) and md5 crypt ($apr1$ prefix).
  42. // If empty HTTP authentication is disabled
  43. AuthUserFile string `json:"auth_user_file" mapstructure:"auth_user_file"`
  44. // If files containing a certificate and matching private key for the server are provided the server will expect
  45. // HTTPS connections.
  46. // Certificate and key files can be reloaded on demand sending a "SIGHUP" signal on Unix based systems and a
  47. // "paramchange" request to the running service on Windows.
  48. CertificateFile string `json:"certificate_file" mapstructure:"certificate_file"`
  49. CertificateKeyFile string `json:"certificate_key_file" mapstructure:"certificate_key_file"`
  50. // TLSCipherSuites is a list of supported cipher suites for TLS version 1.2.
  51. // If CipherSuites is nil/empty, a default list of secure cipher suites
  52. // is used, with a preference order based on hardware performance.
  53. // Note that TLS 1.3 ciphersuites are not configurable.
  54. // The supported ciphersuites names are defined here:
  55. //
  56. // https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52
  57. //
  58. // any invalid name will be silently ignored.
  59. // The order matters, the ciphers listed first will be the preferred ones.
  60. TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
  61. }
  62. // ShouldBind returns true if there service must be started
  63. func (c Conf) ShouldBind() bool {
  64. if c.BindPort > 0 {
  65. return true
  66. }
  67. if filepath.IsAbs(c.BindAddress) && runtime.GOOS != "windows" {
  68. return true
  69. }
  70. return false
  71. }
  72. // Initialize configures and starts the telemetry server.
  73. func (c Conf) Initialize(configDir string) error {
  74. var err error
  75. logger.Debug(logSender, "", "initializing telemetry server with config %+v", c)
  76. authUserFile := getConfigPath(c.AuthUserFile, configDir)
  77. httpAuth, err = common.NewBasicAuthProvider(authUserFile)
  78. if err != nil {
  79. return err
  80. }
  81. certificateFile := getConfigPath(c.CertificateFile, configDir)
  82. certificateKeyFile := getConfigPath(c.CertificateKeyFile, configDir)
  83. initializeRouter(c.EnableProfiler)
  84. httpServer := &http.Server{
  85. Handler: router,
  86. ReadHeaderTimeout: 30 * time.Second,
  87. ReadTimeout: 60 * time.Second,
  88. WriteTimeout: 60 * time.Second,
  89. IdleTimeout: 60 * time.Second,
  90. MaxHeaderBytes: 1 << 14, // 16KB
  91. ErrorLog: log.New(&sdklogger.StdLoggerWrapper{Sender: logSender}, "", 0),
  92. }
  93. if certificateFile != "" && certificateKeyFile != "" {
  94. certMgr, err = common.NewCertManager(certificateFile, certificateKeyFile, configDir, logSender)
  95. if err != nil {
  96. return err
  97. }
  98. config := &tls.Config{
  99. GetCertificate: certMgr.GetCertificateFunc(),
  100. MinVersion: tls.VersionTLS12,
  101. NextProtos: []string{"http/1.1", "h2"},
  102. CipherSuites: util.GetTLSCiphersFromNames(c.TLSCipherSuites),
  103. PreferServerCipherSuites: true,
  104. }
  105. logger.Debug(logSender, "", "configured TLS cipher suites: %v", config.CipherSuites)
  106. httpServer.TLSConfig = config
  107. return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true, logSender)
  108. }
  109. return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false, logSender)
  110. }
  111. // ReloadCertificateMgr reloads the certificate manager
  112. func ReloadCertificateMgr() error {
  113. if certMgr != nil {
  114. return certMgr.Reload()
  115. }
  116. return nil
  117. }
  118. func getConfigPath(name, configDir string) string {
  119. if !util.IsFileInputValid(name) {
  120. return ""
  121. }
  122. if name != "" && !filepath.IsAbs(name) {
  123. return filepath.Join(configDir, name)
  124. }
  125. return name
  126. }