2022-07-17 18:16:00 +00:00
|
|
|
// Copyright (C) 2019-2022 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/>.
|
|
|
|
|
2020-12-18 08:01:19 +00:00
|
|
|
// Package telemetry provides telemetry information for SFTPGo, such as:
|
|
|
|
// - health information (for health checks)
|
|
|
|
// - metrics
|
|
|
|
// - profiling information
|
|
|
|
package telemetry
|
|
|
|
|
|
|
|
import (
|
2020-12-18 15:04:42 +00:00
|
|
|
"crypto/tls"
|
2021-01-03 09:38:28 +00:00
|
|
|
"log"
|
2020-12-18 08:01:19 +00:00
|
|
|
"net/http"
|
2020-12-18 15:04:42 +00:00
|
|
|
"path/filepath"
|
2020-12-29 18:02:56 +00:00
|
|
|
"runtime"
|
2020-12-18 08:01:19 +00:00
|
|
|
"time"
|
|
|
|
|
2021-03-01 18:28:11 +00:00
|
|
|
"github.com/go-chi/chi/v5"
|
2020-12-18 08:01:19 +00:00
|
|
|
|
2021-06-26 05:31:41 +00:00
|
|
|
"github.com/drakkan/sftpgo/v2/common"
|
|
|
|
"github.com/drakkan/sftpgo/v2/logger"
|
2021-07-11 13:26:51 +00:00
|
|
|
"github.com/drakkan/sftpgo/v2/util"
|
2020-12-18 08:01:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
logSender = "telemetry"
|
|
|
|
metricsPath = "/metrics"
|
|
|
|
pprofBasePath = "/debug"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-12-18 15:04:42 +00:00
|
|
|
router *chi.Mux
|
|
|
|
httpAuth common.HTTPAuthProvider
|
|
|
|
certMgr *common.CertManager
|
2020-12-18 08:01:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Conf telemetry server configuration.
|
|
|
|
type Conf struct {
|
2022-03-27 14:32:21 +00:00
|
|
|
// The port used for serving HTTP requests. 0 disable the HTTP server. Default: 0
|
2020-12-18 08:01:19 +00:00
|
|
|
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"`
|
2020-12-18 15:04:42 +00:00
|
|
|
// 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"`
|
2021-02-18 19:17:16 +00:00
|
|
|
// 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"`
|
2022-02-13 14:56:07 +00:00
|
|
|
// 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"`
|
2020-12-18 08:01:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 18:02:56 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2020-12-18 08:01:19 +00:00
|
|
|
// Initialize configures and starts the telemetry server.
|
2020-12-18 15:04:42 +00:00
|
|
|
func (c Conf) Initialize(configDir string) error {
|
|
|
|
var err error
|
2022-01-05 12:22:49 +00:00
|
|
|
logger.Info(logSender, "", "initializing telemetry server with config %+v", c)
|
2020-12-18 15:04:42 +00:00
|
|
|
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)
|
2020-12-18 08:01:19 +00:00
|
|
|
httpServer := &http.Server{
|
2021-05-07 18:41:20 +00:00
|
|
|
Handler: router,
|
|
|
|
ReadHeaderTimeout: 30 * time.Second,
|
|
|
|
ReadTimeout: 60 * time.Second,
|
|
|
|
WriteTimeout: 60 * time.Second,
|
|
|
|
IdleTimeout: 60 * time.Second,
|
|
|
|
MaxHeaderBytes: 1 << 14, // 16KB
|
2022-01-05 10:37:45 +00:00
|
|
|
ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0),
|
2020-12-18 08:01:19 +00:00
|
|
|
}
|
2020-12-18 15:04:42 +00:00
|
|
|
if certificateFile != "" && certificateKeyFile != "" {
|
2022-05-21 14:34:47 +00:00
|
|
|
keyPairs := []common.TLSKeyPair{
|
|
|
|
{
|
|
|
|
Cert: certificateFile,
|
|
|
|
Key: certificateKeyFile,
|
|
|
|
ID: common.DefaultTLSKeyPaidID,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
certMgr, err = common.NewCertManager(keyPairs, configDir, logSender)
|
2020-12-18 15:04:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
config := &tls.Config{
|
2022-05-21 14:34:47 +00:00
|
|
|
GetCertificate: certMgr.GetCertificateFunc(common.DefaultTLSKeyPaidID),
|
2022-02-13 14:56:07 +00:00
|
|
|
MinVersion: util.GetTLSVersion(c.MinTLSVersion),
|
2021-12-08 18:25:22 +00:00
|
|
|
NextProtos: []string{"http/1.1", "h2"},
|
2021-07-11 13:26:51 +00:00
|
|
|
CipherSuites: util.GetTLSCiphersFromNames(c.TLSCipherSuites),
|
2021-02-18 19:17:16 +00:00
|
|
|
PreferServerCipherSuites: true,
|
2020-12-18 15:04:42 +00:00
|
|
|
}
|
2021-02-18 19:17:16 +00:00
|
|
|
logger.Debug(logSender, "", "configured TLS cipher suites: %v", config.CipherSuites)
|
2020-12-18 15:04:42 +00:00
|
|
|
httpServer.TLSConfig = config
|
2021-07-11 13:26:51 +00:00
|
|
|
return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true, logSender)
|
2020-12-18 15:04:42 +00:00
|
|
|
}
|
2021-07-11 13:26:51 +00:00
|
|
|
return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false, logSender)
|
2020-12-18 08:01:19 +00:00
|
|
|
}
|
2020-12-18 15:04:42 +00:00
|
|
|
|
2021-01-03 16:03:04 +00:00
|
|
|
// ReloadCertificateMgr reloads the certificate manager
|
|
|
|
func ReloadCertificateMgr() error {
|
2020-12-18 15:04:42 +00:00
|
|
|
if certMgr != nil {
|
2021-01-03 16:03:04 +00:00
|
|
|
return certMgr.Reload()
|
2020-12-18 15:04:42 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getConfigPath(name, configDir string) string {
|
2021-07-11 13:26:51 +00:00
|
|
|
if !util.IsFileInputValid(name) {
|
2020-12-18 15:04:42 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
if name != "" && !filepath.IsAbs(name) {
|
|
|
|
return filepath.Join(configDir, name)
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|