mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 14:50:55 +03:00
add a startup hook
This commit is contained in:
@@ -312,6 +312,11 @@ type Configuration struct {
|
|||||||
// If proxy protocol is set to 2 and we receive a proxy header from an IP that is not in the list then the
|
// If proxy protocol is set to 2 and we receive a proxy header from an IP that is not in the list then the
|
||||||
// connection will be rejected.
|
// connection will be rejected.
|
||||||
ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
|
ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
|
||||||
|
// Absolute path to an external program or an HTTP URL to invoke as soon as SFTPGo starts.
|
||||||
|
// If you define an HTTP URL it will be invoked using a `GET` request.
|
||||||
|
// Please note that SFTPGo services may not yet be available when this hook is run.
|
||||||
|
// Leave empty do disable.
|
||||||
|
StartupHook string `json:"startup_hook" mapstructure:"startup_hook"`
|
||||||
// Absolute path to an external program or an HTTP URL to invoke after a user connects
|
// Absolute path to an external program or an HTTP URL to invoke after a user connects
|
||||||
// and before he tries to login. It allows you to reject the connection based on the source
|
// and before he tries to login. It allows you to reject the connection based on the source
|
||||||
// ip address. Leave empty do disable.
|
// ip address. Leave empty do disable.
|
||||||
@@ -363,6 +368,43 @@ func (c *Configuration) GetProxyListener(listener net.Listener) (*proxyproto.Lis
|
|||||||
return proxyListener, nil
|
return proxyListener, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecuteStartupHook runs the startup hook if defined
|
||||||
|
func (c *Configuration) ExecuteStartupHook() error {
|
||||||
|
if c.StartupHook == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(c.StartupHook, "http") {
|
||||||
|
var url *url.URL
|
||||||
|
url, err := url.Parse(c.StartupHook)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn(logSender, "", "Invalid startup hook %#v: %v", c.StartupHook, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
startTime := time.Now()
|
||||||
|
httpClient := httpclient.GetRetraybleHTTPClient()
|
||||||
|
resp, err := httpClient.Get(url.String())
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn(logSender, "", "Error executing startup hook: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, response code: %v", time.Since(startTime), resp.StatusCode)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(c.StartupHook) {
|
||||||
|
err := fmt.Errorf("invalid startup hook %#v", c.StartupHook)
|
||||||
|
logger.Warn(logSender, "", "Invalid startup hook %#v", c.StartupHook)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
startTime := time.Now()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, c.StartupHook)
|
||||||
|
err := cmd.Run()
|
||||||
|
logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, error: %v", time.Since(startTime), err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ExecutePostConnectHook executes the post connect hook if defined
|
// ExecutePostConnectHook executes the post connect hook if defined
|
||||||
func (c *Configuration) ExecutePostConnectHook(ipAddr, protocol string) error {
|
func (c *Configuration) ExecutePostConnectHook(ipAddr, protocol string) error {
|
||||||
if c.PostConnectHook == "" {
|
if c.PostConnectHook == "" {
|
||||||
|
|||||||
@@ -449,6 +449,33 @@ func TestProxyProtocolVersion(t *testing.T) {
|
|||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStartupHook(t *testing.T) {
|
||||||
|
Config.StartupHook = ""
|
||||||
|
|
||||||
|
assert.NoError(t, Config.ExecuteStartupHook())
|
||||||
|
|
||||||
|
Config.StartupHook = "http://foo\x7f.com/startup"
|
||||||
|
assert.Error(t, Config.ExecuteStartupHook())
|
||||||
|
|
||||||
|
Config.StartupHook = "http://invalid:5678/"
|
||||||
|
assert.Error(t, Config.ExecuteStartupHook())
|
||||||
|
|
||||||
|
Config.StartupHook = fmt.Sprintf("http://%v", httpAddr)
|
||||||
|
assert.NoError(t, Config.ExecuteStartupHook())
|
||||||
|
|
||||||
|
Config.StartupHook = "invalidhook"
|
||||||
|
assert.Error(t, Config.ExecuteStartupHook())
|
||||||
|
|
||||||
|
if runtime.GOOS != osWindows {
|
||||||
|
hookCmd, err := exec.LookPath("true")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
Config.StartupHook = hookCmd
|
||||||
|
assert.NoError(t, Config.ExecuteStartupHook())
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.StartupHook = ""
|
||||||
|
}
|
||||||
|
|
||||||
func TestPostConnectHook(t *testing.T) {
|
func TestPostConnectHook(t *testing.T) {
|
||||||
Config.PostConnectHook = ""
|
Config.PostConnectHook = ""
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ The configuration file contains the following sections:
|
|||||||
- `proxy_allowed`, List of IP addresses and IP ranges allowed to send the proxy header:
|
- `proxy_allowed`, List of IP addresses and IP ranges allowed to send the proxy header:
|
||||||
- If `proxy_protocol` is set to 1 and we receive a proxy header from an IP that is not in the list then the connection will be accepted and the header will be ignored
|
- If `proxy_protocol` is set to 1 and we receive a proxy header from an IP that is not in the list then the connection will be accepted and the header will be ignored
|
||||||
- If `proxy_protocol` is set to 2 and we receive a proxy header from an IP that is not in the list then the connection will be rejected
|
- If `proxy_protocol` is set to 2 and we receive a proxy header from an IP that is not in the list then the connection will be rejected
|
||||||
|
- `startup_hook`, string. Absolute path to an external program or an HTTP URL to invoke as soon as SFTPGo starts. If you define an HTTP URL it will be invoked using a `GET` request. Please note that SFTPGo services may not yet be available when this hook is run. Leave empty do disable
|
||||||
- `post_connect_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Post connect hook](./post-connect-hook.md) for more details. Leave empty to disable
|
- `post_connect_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Post connect hook](./post-connect-hook.md) for more details. Leave empty to disable
|
||||||
- `max_total_connections`, integer. Maximum number of concurrent client connections. 0 means unlimited
|
- `max_total_connections`, integer. Maximum number of concurrent client connections. 0 means unlimited
|
||||||
- `defender`, struct containing the defender configuration. See [Defender](./defender.md) for more details.
|
- `defender`, struct containing the defender configuration. See [Defender](./defender.md) for more details.
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ func (s *Service) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.startServices()
|
s.startServices()
|
||||||
|
go common.Config.ExecuteStartupHook() //nolint:errcheck
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"setstat_mode": 0,
|
"setstat_mode": 0,
|
||||||
"proxy_protocol": 0,
|
"proxy_protocol": 0,
|
||||||
"proxy_allowed": [],
|
"proxy_allowed": [],
|
||||||
|
"startup_hook": "",
|
||||||
"post_connect_hook": "",
|
"post_connect_hook": "",
|
||||||
"max_total_connections": 0,
|
"max_total_connections": 0,
|
||||||
"defender": {
|
"defender": {
|
||||||
|
|||||||
Reference in New Issue
Block a user