mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
parent
40e759c983
commit
0966d44c0f
8 changed files with 102 additions and 13 deletions
|
@ -175,7 +175,7 @@ The configuration file contains the following sections:
|
|||
- `update_mode`, integer. Defines how the database will be initialized/updated. 0 means automatically. 1 means manually using the initprovider sub-command.
|
||||
- **"httpd"**, the configuration for the HTTP server used to serve REST API and to expose the built-in web interface
|
||||
- `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 8080
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. Default: "127.0.0.1"
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: "127.0.0.1"
|
||||
- `templates_path`, string. Path to the HTML web templates. This can be an absolute path or a path relative to the config dir
|
||||
- `static_files_path`, string. Path to the static files for the web interface. This can be an absolute path or a path relative to the config dir. If both `templates_path` and `static_files_path` are empty the built-in web interface will be disabled
|
||||
- `backups_path`, string. Path to the backup directory. This can be an absolute path or a path relative to the config dir. We don't allow backups in arbitrary paths for security reasons
|
||||
|
@ -184,7 +184,7 @@ The configuration file contains the following sections:
|
|||
- `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. If both the certificate and the private key 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.
|
||||
- **"telemetry"**, the configuration for the telemetry server, more details [below](#telemetry-server)
|
||||
- `bind_port`, integer. The port used for serving HTTP requests. Set to 0 to disable HTTP server. Default: 10000
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. Default: "127.0.0.1"
|
||||
- `bind_address`, string. Leave blank to listen on all available network interfaces. On \*NIX you can specify an absolute path to listen on a Unix-domain socket. Default: "127.0.0.1"
|
||||
- `enable_profiler`, boolean. Enable the built-in profiler. Default `false`
|
||||
- `auth_user_file`, string. Path to a file used to store usernames and passwords 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. Authentication will be always disabled for the `/healthz` endpoint.
|
||||
- `certificate_file`, string. Certificate for HTTPS. This can be an absolute path or a path relative to the config dir.
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
@ -50,6 +51,7 @@ const (
|
|||
// MaxRestoreSize defines the max size for the loaddata input file
|
||||
MaxRestoreSize = 10485760 // 10 MB
|
||||
maxRequestSize = 1048576 // 1MB
|
||||
osWindows = "windows"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -99,6 +101,17 @@ type apiResponse struct {
|
|||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// 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 != osWindows {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Initialize configures and starts the HTTP server
|
||||
func (c Conf) Initialize(configDir string) error {
|
||||
var err error
|
||||
|
@ -128,7 +141,6 @@ func (c Conf) Initialize(configDir string) error {
|
|||
}
|
||||
initializeRouter(staticFilesPath, enableWebAdmin)
|
||||
httpServer := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", c.BindAddress, c.BindPort),
|
||||
Handler: router,
|
||||
ReadTimeout: 60 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
|
@ -145,9 +157,9 @@ func (c Conf) Initialize(configDir string) error {
|
|||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
httpServer.TLSConfig = config
|
||||
return httpServer.ListenAndServeTLS("", "")
|
||||
return utils.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true)
|
||||
}
|
||||
return httpServer.ListenAndServe()
|
||||
return utils.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false)
|
||||
}
|
||||
|
||||
// ReloadTLSCertificate reloads the TLS certificate and key from the configured paths
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/drakkan/sftpgo/common"
|
||||
"github.com/drakkan/sftpgo/dataprovider"
|
||||
|
@ -29,6 +30,21 @@ const (
|
|||
inactiveURL = "http://127.0.0.1:12345"
|
||||
)
|
||||
|
||||
func TestShouldBind(t *testing.T) {
|
||||
c := Conf{
|
||||
BindPort: 10000,
|
||||
}
|
||||
require.True(t, c.ShouldBind())
|
||||
|
||||
c.BindPort = 0
|
||||
require.False(t, c.ShouldBind())
|
||||
|
||||
if runtime.GOOS != osWindows {
|
||||
c.BindAddress = "/absolute/path"
|
||||
require.True(t, c.ShouldBind())
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRespStatus(t *testing.T) {
|
||||
var err error
|
||||
err = &dataprovider.MethodDisabledError{}
|
||||
|
@ -631,7 +647,7 @@ func TestBasicAuth(t *testing.T) {
|
|||
SetBaseURLAndCredentials(httpBaseURL, "test3", "password2")
|
||||
_, _, err = GetVersion(http.StatusUnauthorized)
|
||||
assert.NoError(t, err)
|
||||
if runtime.GOOS != "windows" {
|
||||
if runtime.GOOS != osWindows {
|
||||
authUserData = append(authUserData, []byte("test5:$apr1$gLnIkRIf$Xr/6aJfmIrihP4b2N2tcs/\n")...)
|
||||
err = ioutil.WriteFile(authUserFile, authUserData, os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
NFPM_VERSION=2.1.0
|
||||
NFPM_VERSION=2.1.1
|
||||
NFPM_ARCH=${NFPM_ARCH:-amd64}
|
||||
if [ -z ${SFTPGO_VERSION} ]
|
||||
then
|
||||
|
|
|
@ -143,7 +143,7 @@ func (s *Service) startServices() {
|
|||
logger.Debug(logSender, "", "SFTP server not started, disabled in config file")
|
||||
}
|
||||
|
||||
if httpdConf.BindPort > 0 {
|
||||
if httpdConf.ShouldBind() {
|
||||
go func() {
|
||||
if err := httpdConf.Initialize(s.ConfigDir); err != nil {
|
||||
logger.Error(logSender, "", "could not start HTTP server: %v", err)
|
||||
|
@ -182,7 +182,7 @@ func (s *Service) startServices() {
|
|||
} else {
|
||||
logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
|
||||
}
|
||||
if telemetryConf.BindPort > 0 {
|
||||
if telemetryConf.ShouldBind() {
|
||||
go func() {
|
||||
if err := telemetryConf.Initialize(s.ConfigDir); err != nil {
|
||||
logger.Error(logSender, "", "could not start telemetry server: %v", err)
|
||||
|
|
|
@ -6,9 +6,9 @@ package telemetry
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
|
@ -53,6 +53,17 @@ type Conf struct {
|
|||
CertificateKeyFile string `json:"certificate_key_file" mapstructure:"certificate_key_file"`
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -66,7 +77,6 @@ func (c Conf) Initialize(configDir string) error {
|
|||
certificateKeyFile := getConfigPath(c.CertificateKeyFile, configDir)
|
||||
initializeRouter(c.EnableProfiler)
|
||||
httpServer := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", c.BindAddress, c.BindPort),
|
||||
Handler: router,
|
||||
ReadTimeout: 60 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
|
@ -83,9 +93,9 @@ func (c Conf) Initialize(configDir string) error {
|
|||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
httpServer.TLSConfig = config
|
||||
return httpServer.ListenAndServeTLS("", "")
|
||||
return utils.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true)
|
||||
}
|
||||
return httpServer.ListenAndServe()
|
||||
return utils.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false)
|
||||
}
|
||||
|
||||
// ReloadTLSCertificate reloads the TLS certificate and key from the configured paths
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -84,6 +85,22 @@ func TestInitialization(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestShouldBind(t *testing.T) {
|
||||
c := Conf{
|
||||
BindPort: 10000,
|
||||
EnableProfiler: false,
|
||||
}
|
||||
require.True(t, c.ShouldBind())
|
||||
|
||||
c.BindPort = 0
|
||||
require.False(t, c.ShouldBind())
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
c.BindAddress = "/absolute/path"
|
||||
require.True(t, c.ShouldBind())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouter(t *testing.T) {
|
||||
authUserFile := filepath.Join(os.TempDir(), "http_users.txt")
|
||||
authUserData := []byte("test1:$2y$05$bcHSED7aO1cfLto6ZdDBOOKzlwftslVhtpIkRhAtSa4GuLmk5mola\n")
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
@ -381,3 +382,36 @@ func createDirPathIfMissing(file string, perm os.FileMode) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTPListenAndServe is a wrapper for ListenAndServe that support both tcp
|
||||
// and Unix-domain sockets
|
||||
func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool) error {
|
||||
var listener net.Listener
|
||||
var err error
|
||||
|
||||
if filepath.IsAbs(address) && runtime.GOOS != "windows" {
|
||||
if !IsFileInputValid(address) {
|
||||
return fmt.Errorf("invalid socket address %#v", address)
|
||||
}
|
||||
err = createDirPathIfMissing(address, os.ModePerm)
|
||||
if err != nil {
|
||||
logger.ErrorToConsole("error creating Unix-domain socket parent dir: %v", err)
|
||||
logger.Error(logSender, "", "error creating Unix-domain socket parent dir: %v", err)
|
||||
}
|
||||
os.Remove(address)
|
||||
|
||||
listener, err = net.Listen("unix", address)
|
||||
} else {
|
||||
listener, err = net.Listen("tcp", fmt.Sprintf("%s:%d", address, port))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer listener.Close()
|
||||
|
||||
if isTLS {
|
||||
return srv.ServeTLS(listener, "", "")
|
||||
}
|
||||
return srv.Serve(listener)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue