123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // Copyright (C) 2019-2023 Nicola Murino
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License as published
- // by the Free Software Foundation, version 3.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- // Package telemetry provides telemetry information for SFTPGo, such as:
- // - health information (for health checks)
- // - metrics
- // - profiling information
- package telemetry
- import (
- "crypto/tls"
- "log"
- "net/http"
- "path/filepath"
- "runtime"
- "time"
- "github.com/go-chi/chi/v5"
- "github.com/drakkan/sftpgo/v2/internal/common"
- "github.com/drakkan/sftpgo/v2/internal/logger"
- "github.com/drakkan/sftpgo/v2/internal/util"
- )
- const (
- logSender = "telemetry"
- metricsPath = "/metrics"
- pprofBasePath = "/debug"
- )
- var (
- router *chi.Mux
- httpAuth common.HTTPAuthProvider
- certMgr *common.CertManager
- )
- // Conf telemetry server configuration.
- type Conf struct {
- // The port used for serving HTTP requests. 0 disable the HTTP server. Default: 0
- BindPort int `json:"bind_port" mapstructure:"bind_port"`
- // The address to listen on. A blank value means listen on all available network interfaces. Default: "127.0.0.1"
- BindAddress string `json:"bind_address" mapstructure:"bind_address"`
- // Enable the built-in profiler.
- // The profiler will be accessible via HTTP/HTTPS using the base URL "/debug/pprof/"
- EnableProfiler bool `json:"enable_profiler" mapstructure:"enable_profiler"`
- // Path to a file used to store usernames and password for basic authentication.
- // This can be an absolute path or a path relative to the config dir.
- // We support HTTP basic authentication and the file format must conform to the one generated using the Apache
- // htpasswd tool. The supported password formats are bcrypt ($2y$ prefix) and md5 crypt ($apr1$ prefix).
- // If empty HTTP authentication is disabled
- AuthUserFile string `json:"auth_user_file" mapstructure:"auth_user_file"`
- // If files containing a certificate and matching private key for the server are provided the server will expect
- // HTTPS connections.
- // Certificate and key files can be reloaded on demand sending a "SIGHUP" signal on Unix based systems and a
- // "paramchange" request to the running service on Windows.
- CertificateFile string `json:"certificate_file" mapstructure:"certificate_file"`
- CertificateKeyFile string `json:"certificate_key_file" mapstructure:"certificate_key_file"`
- // TLSCipherSuites is a list of supported cipher suites for TLS version 1.2.
- // If CipherSuites is nil/empty, a default list of secure cipher suites
- // is used, with a preference order based on hardware performance.
- // Note that TLS 1.3 ciphersuites are not configurable.
- // The supported ciphersuites names are defined here:
- //
- // https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52
- //
- // any invalid name will be silently ignored.
- // The order matters, the ciphers listed first will be the preferred ones.
- TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
- // Defines the minimum TLS version. 13 means TLS 1.3, default is TLS 1.2
- MinTLSVersion int `json:"min_tls_version" mapstructure:"min_tls_version"`
- }
- // ShouldBind returns true if there service must be started
- func (c Conf) ShouldBind() bool {
- if c.BindPort > 0 {
- return true
- }
- if filepath.IsAbs(c.BindAddress) && runtime.GOOS != "windows" {
- return true
- }
- return false
- }
- // Initialize configures and starts the telemetry server.
- func (c Conf) Initialize(configDir string) error {
- var err error
- logger.Info(logSender, "", "initializing telemetry server with config %+v", c)
- authUserFile := getConfigPath(c.AuthUserFile, configDir)
- httpAuth, err = common.NewBasicAuthProvider(authUserFile)
- if err != nil {
- return err
- }
- certificateFile := getConfigPath(c.CertificateFile, configDir)
- certificateKeyFile := getConfigPath(c.CertificateKeyFile, configDir)
- initializeRouter(c.EnableProfiler)
- httpServer := &http.Server{
- Handler: router,
- ReadHeaderTimeout: 30 * time.Second,
- ReadTimeout: 60 * time.Second,
- WriteTimeout: 60 * time.Second,
- IdleTimeout: 60 * time.Second,
- MaxHeaderBytes: 1 << 14, // 16KB
- ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0),
- }
- if certificateFile != "" && certificateKeyFile != "" {
- keyPairs := []common.TLSKeyPair{
- {
- Cert: certificateFile,
- Key: certificateKeyFile,
- ID: common.DefaultTLSKeyPaidID,
- },
- }
- certMgr, err = common.NewCertManager(keyPairs, configDir, logSender)
- if err != nil {
- return err
- }
- config := &tls.Config{
- GetCertificate: certMgr.GetCertificateFunc(common.DefaultTLSKeyPaidID),
- MinVersion: util.GetTLSVersion(c.MinTLSVersion),
- NextProtos: []string{"http/1.1", "h2"},
- CipherSuites: util.GetTLSCiphersFromNames(c.TLSCipherSuites),
- PreferServerCipherSuites: true,
- }
- logger.Debug(logSender, "", "configured TLS cipher suites: %v", config.CipherSuites)
- httpServer.TLSConfig = config
- return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true, logSender)
- }
- return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false, logSender)
- }
- // ReloadCertificateMgr reloads the certificate manager
- func ReloadCertificateMgr() error {
- if certMgr != nil {
- return certMgr.Reload()
- }
- return nil
- }
- func getConfigPath(name, configDir string) string {
- if !util.IsFileInputValid(name) {
- return ""
- }
- if name != "" && !filepath.IsAbs(name) {
- return filepath.Join(configDir, name)
- }
- return name
- }
|