common.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. // Package common defines code shared among file transfer packages and protocols
  2. package common
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "os/exec"
  12. "path/filepath"
  13. "strings"
  14. "sync"
  15. "sync/atomic"
  16. "time"
  17. "github.com/pires/go-proxyproto"
  18. "github.com/drakkan/sftpgo/dataprovider"
  19. "github.com/drakkan/sftpgo/httpclient"
  20. "github.com/drakkan/sftpgo/logger"
  21. "github.com/drakkan/sftpgo/metrics"
  22. "github.com/drakkan/sftpgo/utils"
  23. "github.com/drakkan/sftpgo/vfs"
  24. )
  25. // constants
  26. const (
  27. logSender = "common"
  28. uploadLogSender = "Upload"
  29. downloadLogSender = "Download"
  30. renameLogSender = "Rename"
  31. rmdirLogSender = "Rmdir"
  32. mkdirLogSender = "Mkdir"
  33. symlinkLogSender = "Symlink"
  34. removeLogSender = "Remove"
  35. chownLogSender = "Chown"
  36. chmodLogSender = "Chmod"
  37. chtimesLogSender = "Chtimes"
  38. truncateLogSender = "Truncate"
  39. operationDownload = "download"
  40. operationUpload = "upload"
  41. operationDelete = "delete"
  42. // Pre-download action name
  43. OperationPreDownload = "pre-download"
  44. // Pre-upload action name
  45. OperationPreUpload = "pre-upload"
  46. operationPreDelete = "pre-delete"
  47. operationRename = "rename"
  48. // SSH command action name
  49. OperationSSHCmd = "ssh_cmd"
  50. chtimesFormat = "2006-01-02T15:04:05" // YYYY-MM-DDTHH:MM:SS
  51. idleTimeoutCheckInterval = 3 * time.Minute
  52. )
  53. // Stat flags
  54. const (
  55. StatAttrUIDGID = 1
  56. StatAttrPerms = 2
  57. StatAttrTimes = 4
  58. StatAttrSize = 8
  59. )
  60. // Transfer types
  61. const (
  62. TransferUpload = iota
  63. TransferDownload
  64. )
  65. // Supported protocols
  66. const (
  67. ProtocolSFTP = "SFTP"
  68. ProtocolSCP = "SCP"
  69. ProtocolSSH = "SSH"
  70. ProtocolFTP = "FTP"
  71. ProtocolWebDAV = "DAV"
  72. ProtocolHTTP = "HTTP"
  73. )
  74. // Upload modes
  75. const (
  76. UploadModeStandard = iota
  77. UploadModeAtomic
  78. UploadModeAtomicWithResume
  79. )
  80. func init() {
  81. Connections.clients = clientsMap{
  82. clients: make(map[string]int),
  83. }
  84. }
  85. // errors definitions
  86. var (
  87. ErrPermissionDenied = errors.New("permission denied")
  88. ErrNotExist = errors.New("no such file or directory")
  89. ErrOpUnsupported = errors.New("operation unsupported")
  90. ErrGenericFailure = errors.New("failure")
  91. ErrQuotaExceeded = errors.New("denying write due to space limit")
  92. ErrSkipPermissionsCheck = errors.New("permission check skipped")
  93. ErrConnectionDenied = errors.New("you are not allowed to connect")
  94. ErrNoBinding = errors.New("no binding configured")
  95. ErrCrtRevoked = errors.New("your certificate has been revoked")
  96. ErrNoCredentials = errors.New("no credential provided")
  97. ErrInternalFailure = errors.New("internal failure")
  98. errNoTransfer = errors.New("requested transfer not found")
  99. errTransferMismatch = errors.New("transfer mismatch")
  100. )
  101. var (
  102. // Config is the configuration for the supported protocols
  103. Config Configuration
  104. // Connections is the list of active connections
  105. Connections ActiveConnections
  106. // QuotaScans is the list of active quota scans
  107. QuotaScans ActiveScans
  108. idleTimeoutTicker *time.Ticker
  109. idleTimeoutTickerDone chan bool
  110. supportedProtocols = []string{ProtocolSFTP, ProtocolSCP, ProtocolSSH, ProtocolFTP, ProtocolWebDAV, ProtocolHTTP}
  111. // the map key is the protocol, for each protocol we can have multiple rate limiters
  112. rateLimiters map[string][]*rateLimiter
  113. )
  114. // Initialize sets the common configuration
  115. func Initialize(c Configuration) error {
  116. Config = c
  117. Config.idleLoginTimeout = 2 * time.Minute
  118. Config.idleTimeoutAsDuration = time.Duration(Config.IdleTimeout) * time.Minute
  119. if Config.IdleTimeout > 0 {
  120. startIdleTimeoutTicker(idleTimeoutCheckInterval)
  121. }
  122. Config.defender = nil
  123. if c.DefenderConfig.Enabled {
  124. defender, err := newInMemoryDefender(&c.DefenderConfig)
  125. if err != nil {
  126. return fmt.Errorf("defender initialization error: %v", err)
  127. }
  128. logger.Info(logSender, "", "defender initialized with config %+v", c.DefenderConfig)
  129. Config.defender = defender
  130. }
  131. rateLimiters = make(map[string][]*rateLimiter)
  132. for _, rlCfg := range c.RateLimitersConfig {
  133. if rlCfg.isEnabled() {
  134. if err := rlCfg.validate(); err != nil {
  135. return fmt.Errorf("rate limiters initialization error: %v", err)
  136. }
  137. rateLimiter := rlCfg.getLimiter()
  138. for _, protocol := range rlCfg.Protocols {
  139. rateLimiters[protocol] = append(rateLimiters[protocol], rateLimiter)
  140. }
  141. }
  142. }
  143. vfs.SetTempPath(c.TempPath)
  144. dataprovider.SetTempPath(c.TempPath)
  145. return nil
  146. }
  147. // LimitRate blocks until all the configured rate limiters
  148. // allow one event to happen.
  149. // It returns an error if the time to wait exceeds the max
  150. // allowed delay
  151. func LimitRate(protocol, ip string) (time.Duration, error) {
  152. for _, limiter := range rateLimiters[protocol] {
  153. if delay, err := limiter.Wait(ip); err != nil {
  154. logger.Debug(logSender, "", "protocol %v ip %v: %v", protocol, ip, err)
  155. return delay, err
  156. }
  157. }
  158. return 0, nil
  159. }
  160. // ReloadDefender reloads the defender's block and safe lists
  161. func ReloadDefender() error {
  162. if Config.defender == nil {
  163. return nil
  164. }
  165. return Config.defender.Reload()
  166. }
  167. // IsBanned returns true if the specified IP address is banned
  168. func IsBanned(ip string) bool {
  169. if Config.defender == nil {
  170. return false
  171. }
  172. return Config.defender.IsBanned(ip)
  173. }
  174. // GetDefenderBanTime returns the ban time for the given IP
  175. // or nil if the IP is not banned or the defender is disabled
  176. func GetDefenderBanTime(ip string) *time.Time {
  177. if Config.defender == nil {
  178. return nil
  179. }
  180. return Config.defender.GetBanTime(ip)
  181. }
  182. // Unban removes the specified IP address from the banned ones
  183. func Unban(ip string) bool {
  184. if Config.defender == nil {
  185. return false
  186. }
  187. return Config.defender.Unban(ip)
  188. }
  189. // GetDefenderScore returns the score for the given IP
  190. func GetDefenderScore(ip string) int {
  191. if Config.defender == nil {
  192. return 0
  193. }
  194. return Config.defender.GetScore(ip)
  195. }
  196. // AddDefenderEvent adds the specified defender event for the given IP
  197. func AddDefenderEvent(ip string, event HostEvent) {
  198. if Config.defender == nil {
  199. return
  200. }
  201. Config.defender.AddEvent(ip, event)
  202. }
  203. // the ticker cannot be started/stopped from multiple goroutines
  204. func startIdleTimeoutTicker(duration time.Duration) {
  205. stopIdleTimeoutTicker()
  206. idleTimeoutTicker = time.NewTicker(duration)
  207. idleTimeoutTickerDone = make(chan bool)
  208. go func() {
  209. for {
  210. select {
  211. case <-idleTimeoutTickerDone:
  212. return
  213. case <-idleTimeoutTicker.C:
  214. Connections.checkIdles()
  215. }
  216. }
  217. }()
  218. }
  219. func stopIdleTimeoutTicker() {
  220. if idleTimeoutTicker != nil {
  221. idleTimeoutTicker.Stop()
  222. idleTimeoutTickerDone <- true
  223. idleTimeoutTicker = nil
  224. }
  225. }
  226. // ActiveTransfer defines the interface for the current active transfers
  227. type ActiveTransfer interface {
  228. GetID() uint64
  229. GetType() int
  230. GetSize() int64
  231. GetVirtualPath() string
  232. GetStartTime() time.Time
  233. SignalClose()
  234. Truncate(fsPath string, size int64) (int64, error)
  235. GetRealFsPath(fsPath string) string
  236. }
  237. // ActiveConnection defines the interface for the current active connections
  238. type ActiveConnection interface {
  239. GetID() string
  240. GetUsername() string
  241. GetRemoteAddress() string
  242. GetClientVersion() string
  243. GetProtocol() string
  244. GetConnectionTime() time.Time
  245. GetLastActivity() time.Time
  246. GetCommand() string
  247. Disconnect() error
  248. AddTransfer(t ActiveTransfer)
  249. RemoveTransfer(t ActiveTransfer)
  250. GetTransfers() []ConnectionTransfer
  251. CloseFS() error
  252. }
  253. // StatAttributes defines the attributes for set stat commands
  254. type StatAttributes struct {
  255. Mode os.FileMode
  256. Atime time.Time
  257. Mtime time.Time
  258. UID int
  259. GID int
  260. Flags int
  261. Size int64
  262. }
  263. // ConnectionTransfer defines the trasfer details to expose
  264. type ConnectionTransfer struct {
  265. ID uint64 `json:"-"`
  266. OperationType string `json:"operation_type"`
  267. StartTime int64 `json:"start_time"`
  268. Size int64 `json:"size"`
  269. VirtualPath string `json:"path"`
  270. }
  271. func (t *ConnectionTransfer) getConnectionTransferAsString() string {
  272. result := ""
  273. switch t.OperationType {
  274. case operationUpload:
  275. result += "UL "
  276. case operationDownload:
  277. result += "DL "
  278. }
  279. result += fmt.Sprintf("%#v ", t.VirtualPath)
  280. if t.Size > 0 {
  281. elapsed := time.Since(utils.GetTimeFromMsecSinceEpoch(t.StartTime))
  282. speed := float64(t.Size) / float64(utils.GetTimeAsMsSinceEpoch(time.Now())-t.StartTime)
  283. result += fmt.Sprintf("Size: %#v Elapsed: %#v Speed: \"%.1f KB/s\"", utils.ByteCountIEC(t.Size),
  284. utils.GetDurationAsString(elapsed), speed)
  285. }
  286. return result
  287. }
  288. // Configuration defines configuration parameters common to all supported protocols
  289. type Configuration struct {
  290. // Maximum idle timeout as minutes. If a client is idle for a time that exceeds this setting it will be disconnected.
  291. // 0 means disabled
  292. IdleTimeout int `json:"idle_timeout" mapstructure:"idle_timeout"`
  293. // UploadMode 0 means standard, the files are uploaded directly to the requested path.
  294. // 1 means atomic: the files are uploaded to a temporary path and renamed to the requested path
  295. // when the client ends the upload. Atomic mode avoid problems such as a web server that
  296. // serves partial files when the files are being uploaded.
  297. // In atomic mode if there is an upload error the temporary file is deleted and so the requested
  298. // upload path will not contain a partial file.
  299. // 2 means atomic with resume support: as atomic but if there is an upload error the temporary
  300. // file is renamed to the requested path and not deleted, this way a client can reconnect and resume
  301. // the upload.
  302. UploadMode int `json:"upload_mode" mapstructure:"upload_mode"`
  303. // Actions to execute for SFTP file operations and SSH commands
  304. Actions ProtocolActions `json:"actions" mapstructure:"actions"`
  305. // SetstatMode 0 means "normal mode": requests for changing permissions and owner/group are executed.
  306. // 1 means "ignore mode": requests for changing permissions and owner/group are silently ignored.
  307. // 2 means "ignore mode for cloud fs": requests for changing permissions and owner/group/time are
  308. // silently ignored for cloud based filesystem such as S3, GCS, Azure Blob
  309. SetstatMode int `json:"setstat_mode" mapstructure:"setstat_mode"`
  310. // TempPath defines the path for temporary files such as those used for atomic uploads or file pipes.
  311. // If you set this option you must make sure that the defined path exists, is accessible for writing
  312. // by the user running SFTPGo, and is on the same filesystem as the users home directories otherwise
  313. // the renaming for atomic uploads will become a copy and therefore may take a long time.
  314. // The temporary files are not namespaced. The default is generally fine. Leave empty for the default.
  315. TempPath string `json:"temp_path" mapstructure:"temp_path"`
  316. // Support for HAProxy PROXY protocol.
  317. // If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGNIX, you can enable
  318. // the proxy protocol. It provides a convenient way to safely transport connection information
  319. // such as a client's address across multiple layers of NAT or TCP proxies to get the real
  320. // client IP address instead of the proxy IP. Both protocol versions 1 and 2 are supported.
  321. // - 0 means disabled
  322. // - 1 means proxy protocol enabled. Proxy header will be used and requests without proxy header will be accepted.
  323. // - 2 means proxy protocol required. Proxy header will be used and requests without proxy header will be rejected.
  324. // If the proxy protocol is enabled in SFTPGo then you have to enable the protocol in your proxy configuration too,
  325. // for example for HAProxy add "send-proxy" or "send-proxy-v2" to each server configuration line.
  326. ProxyProtocol int `json:"proxy_protocol" mapstructure:"proxy_protocol"`
  327. // List of IP addresses and IP ranges allowed to send the proxy header.
  328. // If proxy protocol is set to 1 and we receive a proxy header from an IP that is not in the list then the
  329. // connection will be accepted and the header will be ignored.
  330. // If proxy protocol is set to 2 and we receive a proxy header from an IP that is not in the list then the
  331. // connection will be rejected.
  332. ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
  333. // Absolute path to an external program or an HTTP URL to invoke as soon as SFTPGo starts.
  334. // If you define an HTTP URL it will be invoked using a `GET` request.
  335. // Please note that SFTPGo services may not yet be available when this hook is run.
  336. // Leave empty do disable.
  337. StartupHook string `json:"startup_hook" mapstructure:"startup_hook"`
  338. // Absolute path to an external program or an HTTP URL to invoke after a user connects
  339. // and before he tries to login. It allows you to reject the connection based on the source
  340. // ip address. Leave empty do disable.
  341. PostConnectHook string `json:"post_connect_hook" mapstructure:"post_connect_hook"`
  342. // Maximum number of concurrent client connections. 0 means unlimited
  343. MaxTotalConnections int `json:"max_total_connections" mapstructure:"max_total_connections"`
  344. // Maximum number of concurrent client connections from the same host (IP). 0 means unlimited
  345. MaxPerHostConnections int `json:"max_per_host_connections" mapstructure:"max_per_host_connections"`
  346. // Defender configuration
  347. DefenderConfig DefenderConfig `json:"defender" mapstructure:"defender"`
  348. // Rate limiter configurations
  349. RateLimitersConfig []RateLimiterConfig `json:"rate_limiters" mapstructure:"rate_limiters"`
  350. idleTimeoutAsDuration time.Duration
  351. idleLoginTimeout time.Duration
  352. defender Defender
  353. }
  354. // IsAtomicUploadEnabled returns true if atomic upload is enabled
  355. func (c *Configuration) IsAtomicUploadEnabled() bool {
  356. return c.UploadMode == UploadModeAtomic || c.UploadMode == UploadModeAtomicWithResume
  357. }
  358. // GetProxyListener returns a wrapper for the given listener that supports the
  359. // HAProxy Proxy Protocol or nil if the proxy protocol is not configured
  360. func (c *Configuration) GetProxyListener(listener net.Listener) (*proxyproto.Listener, error) {
  361. var proxyListener *proxyproto.Listener
  362. var err error
  363. if c.ProxyProtocol > 0 {
  364. var policyFunc func(upstream net.Addr) (proxyproto.Policy, error)
  365. if c.ProxyProtocol == 1 && len(c.ProxyAllowed) > 0 {
  366. policyFunc, err = proxyproto.LaxWhiteListPolicy(c.ProxyAllowed)
  367. if err != nil {
  368. return nil, err
  369. }
  370. }
  371. if c.ProxyProtocol == 2 {
  372. if len(c.ProxyAllowed) == 0 {
  373. policyFunc = func(upstream net.Addr) (proxyproto.Policy, error) {
  374. return proxyproto.REQUIRE, nil
  375. }
  376. } else {
  377. policyFunc, err = proxyproto.StrictWhiteListPolicy(c.ProxyAllowed)
  378. if err != nil {
  379. return nil, err
  380. }
  381. }
  382. }
  383. proxyListener = &proxyproto.Listener{
  384. Listener: listener,
  385. Policy: policyFunc,
  386. }
  387. }
  388. return proxyListener, nil
  389. }
  390. // ExecuteStartupHook runs the startup hook if defined
  391. func (c *Configuration) ExecuteStartupHook() error {
  392. if c.StartupHook == "" {
  393. return nil
  394. }
  395. if strings.HasPrefix(c.StartupHook, "http") {
  396. var url *url.URL
  397. url, err := url.Parse(c.StartupHook)
  398. if err != nil {
  399. logger.Warn(logSender, "", "Invalid startup hook %#v: %v", c.StartupHook, err)
  400. return err
  401. }
  402. startTime := time.Now()
  403. resp, err := httpclient.RetryableGet(url.String())
  404. if err != nil {
  405. logger.Warn(logSender, "", "Error executing startup hook: %v", err)
  406. return err
  407. }
  408. defer resp.Body.Close()
  409. logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, response code: %v", time.Since(startTime), resp.StatusCode)
  410. return nil
  411. }
  412. if !filepath.IsAbs(c.StartupHook) {
  413. err := fmt.Errorf("invalid startup hook %#v", c.StartupHook)
  414. logger.Warn(logSender, "", "Invalid startup hook %#v", c.StartupHook)
  415. return err
  416. }
  417. startTime := time.Now()
  418. ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
  419. defer cancel()
  420. cmd := exec.CommandContext(ctx, c.StartupHook)
  421. err := cmd.Run()
  422. logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, error: %v", time.Since(startTime), err)
  423. return nil
  424. }
  425. // ExecutePostConnectHook executes the post connect hook if defined
  426. func (c *Configuration) ExecutePostConnectHook(ipAddr, protocol string) error {
  427. if c.PostConnectHook == "" {
  428. return nil
  429. }
  430. if strings.HasPrefix(c.PostConnectHook, "http") {
  431. var url *url.URL
  432. url, err := url.Parse(c.PostConnectHook)
  433. if err != nil {
  434. logger.Warn(protocol, "", "Login from ip %#v denied, invalid post connect hook %#v: %v",
  435. ipAddr, c.PostConnectHook, err)
  436. return err
  437. }
  438. q := url.Query()
  439. q.Add("ip", ipAddr)
  440. q.Add("protocol", protocol)
  441. url.RawQuery = q.Encode()
  442. resp, err := httpclient.RetryableGet(url.String())
  443. if err != nil {
  444. logger.Warn(protocol, "", "Login from ip %#v denied, error executing post connect hook: %v", ipAddr, err)
  445. return err
  446. }
  447. defer resp.Body.Close()
  448. if resp.StatusCode != http.StatusOK {
  449. logger.Warn(protocol, "", "Login from ip %#v denied, post connect hook response code: %v", ipAddr, resp.StatusCode)
  450. return errUnexpectedHTTResponse
  451. }
  452. return nil
  453. }
  454. if !filepath.IsAbs(c.PostConnectHook) {
  455. err := fmt.Errorf("invalid post connect hook %#v", c.PostConnectHook)
  456. logger.Warn(protocol, "", "Login from ip %#v denied: %v", ipAddr, err)
  457. return err
  458. }
  459. ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
  460. defer cancel()
  461. cmd := exec.CommandContext(ctx, c.PostConnectHook)
  462. cmd.Env = append(os.Environ(),
  463. fmt.Sprintf("SFTPGO_CONNECTION_IP=%v", ipAddr),
  464. fmt.Sprintf("SFTPGO_CONNECTION_PROTOCOL=%v", protocol))
  465. err := cmd.Run()
  466. if err != nil {
  467. logger.Warn(protocol, "", "Login from ip %#v denied, connect hook error: %v", ipAddr, err)
  468. }
  469. return err
  470. }
  471. // SSHConnection defines an ssh connection.
  472. // Each SSH connection can open several channels for SFTP or SSH commands
  473. type SSHConnection struct {
  474. id string
  475. conn net.Conn
  476. lastActivity int64
  477. }
  478. // NewSSHConnection returns a new SSHConnection
  479. func NewSSHConnection(id string, conn net.Conn) *SSHConnection {
  480. return &SSHConnection{
  481. id: id,
  482. conn: conn,
  483. lastActivity: time.Now().UnixNano(),
  484. }
  485. }
  486. // GetID returns the ID for this SSHConnection
  487. func (c *SSHConnection) GetID() string {
  488. return c.id
  489. }
  490. // UpdateLastActivity updates last activity for this connection
  491. func (c *SSHConnection) UpdateLastActivity() {
  492. atomic.StoreInt64(&c.lastActivity, time.Now().UnixNano())
  493. }
  494. // GetLastActivity returns the last connection activity
  495. func (c *SSHConnection) GetLastActivity() time.Time {
  496. return time.Unix(0, atomic.LoadInt64(&c.lastActivity))
  497. }
  498. // Close closes the underlying network connection
  499. func (c *SSHConnection) Close() error {
  500. return c.conn.Close()
  501. }
  502. // ActiveConnections holds the currect active connections with the associated transfers
  503. type ActiveConnections struct {
  504. // clients contains both authenticated and estabilished connections and the ones waiting
  505. // for authentication
  506. clients clientsMap
  507. sync.RWMutex
  508. connections []ActiveConnection
  509. sshConnections []*SSHConnection
  510. }
  511. // GetActiveSessions returns the number of active sessions for the given username.
  512. // We return the open sessions for any protocol
  513. func (conns *ActiveConnections) GetActiveSessions(username string) int {
  514. conns.RLock()
  515. defer conns.RUnlock()
  516. numSessions := 0
  517. for _, c := range conns.connections {
  518. if c.GetUsername() == username {
  519. numSessions++
  520. }
  521. }
  522. return numSessions
  523. }
  524. // Add adds a new connection to the active ones
  525. func (conns *ActiveConnections) Add(c ActiveConnection) {
  526. conns.Lock()
  527. defer conns.Unlock()
  528. conns.connections = append(conns.connections, c)
  529. metrics.UpdateActiveConnectionsSize(len(conns.connections))
  530. logger.Debug(c.GetProtocol(), c.GetID(), "connection added, num open connections: %v", len(conns.connections))
  531. }
  532. // Swap replaces an existing connection with the given one.
  533. // This method is useful if you have to change some connection details
  534. // for example for FTP is used to update the connection once the user
  535. // authenticates
  536. func (conns *ActiveConnections) Swap(c ActiveConnection) error {
  537. conns.Lock()
  538. defer conns.Unlock()
  539. for idx, conn := range conns.connections {
  540. if conn.GetID() == c.GetID() {
  541. conn = nil
  542. conns.connections[idx] = c
  543. return nil
  544. }
  545. }
  546. return errors.New("connection to swap not found")
  547. }
  548. // Remove removes a connection from the active ones
  549. func (conns *ActiveConnections) Remove(connectionID string) {
  550. conns.Lock()
  551. defer conns.Unlock()
  552. for idx, conn := range conns.connections {
  553. if conn.GetID() == connectionID {
  554. err := conn.CloseFS()
  555. lastIdx := len(conns.connections) - 1
  556. conns.connections[idx] = conns.connections[lastIdx]
  557. conns.connections[lastIdx] = nil
  558. conns.connections = conns.connections[:lastIdx]
  559. metrics.UpdateActiveConnectionsSize(lastIdx)
  560. logger.Debug(conn.GetProtocol(), conn.GetID(), "connection removed, close fs error: %v, num open connections: %v",
  561. err, lastIdx)
  562. return
  563. }
  564. }
  565. logger.Warn(logSender, "", "connection id %#v to remove not found!", connectionID)
  566. }
  567. // Close closes an active connection.
  568. // It returns true on success
  569. func (conns *ActiveConnections) Close(connectionID string) bool {
  570. conns.RLock()
  571. result := false
  572. for _, c := range conns.connections {
  573. if c.GetID() == connectionID {
  574. defer func(conn ActiveConnection) {
  575. err := conn.Disconnect()
  576. logger.Debug(conn.GetProtocol(), conn.GetID(), "close connection requested, close err: %v", err)
  577. }(c)
  578. result = true
  579. break
  580. }
  581. }
  582. conns.RUnlock()
  583. return result
  584. }
  585. // AddSSHConnection adds a new ssh connection to the active ones
  586. func (conns *ActiveConnections) AddSSHConnection(c *SSHConnection) {
  587. conns.Lock()
  588. defer conns.Unlock()
  589. conns.sshConnections = append(conns.sshConnections, c)
  590. logger.Debug(logSender, c.GetID(), "ssh connection added, num open connections: %v", len(conns.sshConnections))
  591. }
  592. // RemoveSSHConnection removes a connection from the active ones
  593. func (conns *ActiveConnections) RemoveSSHConnection(connectionID string) {
  594. conns.Lock()
  595. defer conns.Unlock()
  596. for idx, conn := range conns.sshConnections {
  597. if conn.GetID() == connectionID {
  598. lastIdx := len(conns.sshConnections) - 1
  599. conns.sshConnections[idx] = conns.sshConnections[lastIdx]
  600. conns.sshConnections[lastIdx] = nil
  601. conns.sshConnections = conns.sshConnections[:lastIdx]
  602. logger.Debug(logSender, conn.GetID(), "ssh connection removed, num open ssh connections: %v", lastIdx)
  603. return
  604. }
  605. }
  606. logger.Warn(logSender, "", "ssh connection to remove with id %#v not found!", connectionID)
  607. }
  608. func (conns *ActiveConnections) checkIdles() {
  609. conns.RLock()
  610. for _, sshConn := range conns.sshConnections {
  611. idleTime := time.Since(sshConn.GetLastActivity())
  612. if idleTime > Config.idleTimeoutAsDuration {
  613. // we close the an ssh connection if it has no active connections associated
  614. idToMatch := fmt.Sprintf("_%v_", sshConn.GetID())
  615. toClose := true
  616. for _, conn := range conns.connections {
  617. if strings.Contains(conn.GetID(), idToMatch) {
  618. toClose = false
  619. break
  620. }
  621. }
  622. if toClose {
  623. defer func(c *SSHConnection) {
  624. err := c.Close()
  625. logger.Debug(logSender, c.GetID(), "close idle SSH connection, idle time: %v, close err: %v",
  626. time.Since(c.GetLastActivity()), err)
  627. }(sshConn)
  628. }
  629. }
  630. }
  631. for _, c := range conns.connections {
  632. idleTime := time.Since(c.GetLastActivity())
  633. isUnauthenticatedFTPUser := (c.GetProtocol() == ProtocolFTP && c.GetUsername() == "")
  634. if idleTime > Config.idleTimeoutAsDuration || (isUnauthenticatedFTPUser && idleTime > Config.idleLoginTimeout) {
  635. defer func(conn ActiveConnection, isFTPNoAuth bool) {
  636. err := conn.Disconnect()
  637. logger.Debug(conn.GetProtocol(), conn.GetID(), "close idle connection, idle time: %v, username: %#v close err: %v",
  638. time.Since(conn.GetLastActivity()), conn.GetUsername(), err)
  639. if isFTPNoAuth {
  640. ip := utils.GetIPFromRemoteAddress(c.GetRemoteAddress())
  641. logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTryed, c.GetProtocol(), "client idle")
  642. metrics.AddNoAuthTryed()
  643. AddDefenderEvent(ip, HostEventNoLoginTried)
  644. dataprovider.ExecutePostLoginHook(&dataprovider.User{}, dataprovider.LoginMethodNoAuthTryed, ip, c.GetProtocol(),
  645. dataprovider.ErrNoAuthTryed)
  646. }
  647. }(c, isUnauthenticatedFTPUser)
  648. }
  649. }
  650. conns.RUnlock()
  651. }
  652. // AddClientConnection stores a new client connection
  653. func (conns *ActiveConnections) AddClientConnection(ipAddr string) {
  654. conns.clients.add(ipAddr)
  655. }
  656. // RemoveClientConnection removes a disconnected client from the tracked ones
  657. func (conns *ActiveConnections) RemoveClientConnection(ipAddr string) {
  658. conns.clients.remove(ipAddr)
  659. }
  660. // GetClientConnections returns the total number of client connections
  661. func (conns *ActiveConnections) GetClientConnections() int32 {
  662. return conns.clients.getTotal()
  663. }
  664. // IsNewConnectionAllowed returns false if the maximum number of concurrent allowed connections is exceeded
  665. func (conns *ActiveConnections) IsNewConnectionAllowed(ipAddr string) bool {
  666. if Config.MaxTotalConnections == 0 && Config.MaxPerHostConnections == 0 {
  667. return true
  668. }
  669. if Config.MaxPerHostConnections > 0 {
  670. if total := conns.clients.getTotalFrom(ipAddr); total > Config.MaxPerHostConnections {
  671. logger.Debug(logSender, "", "active connections from %v %v/%v", ipAddr, total, Config.MaxPerHostConnections)
  672. AddDefenderEvent(ipAddr, HostEventLimitExceeded)
  673. return false
  674. }
  675. }
  676. if Config.MaxTotalConnections > 0 {
  677. if total := conns.clients.getTotal(); total > int32(Config.MaxTotalConnections) {
  678. logger.Debug(logSender, "", "active client connections %v/%v", total, Config.MaxTotalConnections)
  679. return false
  680. }
  681. // on a single SFTP connection we could have multiple SFTP channels or commands
  682. // so we check the estabilished connections too
  683. conns.RLock()
  684. defer conns.RUnlock()
  685. return len(conns.connections) < Config.MaxTotalConnections
  686. }
  687. return true
  688. }
  689. // GetStats returns stats for active connections
  690. func (conns *ActiveConnections) GetStats() []*ConnectionStatus {
  691. conns.RLock()
  692. defer conns.RUnlock()
  693. stats := make([]*ConnectionStatus, 0, len(conns.connections))
  694. for _, c := range conns.connections {
  695. stat := &ConnectionStatus{
  696. Username: c.GetUsername(),
  697. ConnectionID: c.GetID(),
  698. ClientVersion: c.GetClientVersion(),
  699. RemoteAddress: c.GetRemoteAddress(),
  700. ConnectionTime: utils.GetTimeAsMsSinceEpoch(c.GetConnectionTime()),
  701. LastActivity: utils.GetTimeAsMsSinceEpoch(c.GetLastActivity()),
  702. Protocol: c.GetProtocol(),
  703. Command: c.GetCommand(),
  704. Transfers: c.GetTransfers(),
  705. }
  706. stats = append(stats, stat)
  707. }
  708. return stats
  709. }
  710. // ConnectionStatus returns the status for an active connection
  711. type ConnectionStatus struct {
  712. // Logged in username
  713. Username string `json:"username"`
  714. // Unique identifier for the connection
  715. ConnectionID string `json:"connection_id"`
  716. // client's version string
  717. ClientVersion string `json:"client_version,omitempty"`
  718. // Remote address for this connection
  719. RemoteAddress string `json:"remote_address"`
  720. // Connection time as unix timestamp in milliseconds
  721. ConnectionTime int64 `json:"connection_time"`
  722. // Last activity as unix timestamp in milliseconds
  723. LastActivity int64 `json:"last_activity"`
  724. // Protocol for this connection
  725. Protocol string `json:"protocol"`
  726. // active uploads/downloads
  727. Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
  728. // SSH command or WebDAV method
  729. Command string `json:"command,omitempty"`
  730. }
  731. // GetConnectionDuration returns the connection duration as string
  732. func (c *ConnectionStatus) GetConnectionDuration() string {
  733. elapsed := time.Since(utils.GetTimeFromMsecSinceEpoch(c.ConnectionTime))
  734. return utils.GetDurationAsString(elapsed)
  735. }
  736. // GetConnectionInfo returns connection info.
  737. // Protocol,Client Version and RemoteAddress are returned.
  738. func (c *ConnectionStatus) GetConnectionInfo() string {
  739. var result strings.Builder
  740. result.WriteString(fmt.Sprintf("%v. Client: %#v From: %#v", c.Protocol, c.ClientVersion, c.RemoteAddress))
  741. if c.Command == "" {
  742. return result.String()
  743. }
  744. switch c.Protocol {
  745. case ProtocolSSH, ProtocolFTP:
  746. result.WriteString(fmt.Sprintf(". Command: %#v", c.Command))
  747. case ProtocolWebDAV:
  748. result.WriteString(fmt.Sprintf(". Method: %#v", c.Command))
  749. }
  750. return result.String()
  751. }
  752. // GetTransfersAsString returns the active transfers as string
  753. func (c *ConnectionStatus) GetTransfersAsString() string {
  754. result := ""
  755. for _, t := range c.Transfers {
  756. if result != "" {
  757. result += ". "
  758. }
  759. result += t.getConnectionTransferAsString()
  760. }
  761. return result
  762. }
  763. // ActiveQuotaScan defines an active quota scan for a user home dir
  764. type ActiveQuotaScan struct {
  765. // Username to which the quota scan refers
  766. Username string `json:"username"`
  767. // quota scan start time as unix timestamp in milliseconds
  768. StartTime int64 `json:"start_time"`
  769. }
  770. // ActiveVirtualFolderQuotaScan defines an active quota scan for a virtual folder
  771. type ActiveVirtualFolderQuotaScan struct {
  772. // folder name to which the quota scan refers
  773. Name string `json:"name"`
  774. // quota scan start time as unix timestamp in milliseconds
  775. StartTime int64 `json:"start_time"`
  776. }
  777. // ActiveScans holds the active quota scans
  778. type ActiveScans struct {
  779. sync.RWMutex
  780. UserHomeScans []ActiveQuotaScan
  781. FolderScans []ActiveVirtualFolderQuotaScan
  782. }
  783. // GetUsersQuotaScans returns the active quota scans for users home directories
  784. func (s *ActiveScans) GetUsersQuotaScans() []ActiveQuotaScan {
  785. s.RLock()
  786. defer s.RUnlock()
  787. scans := make([]ActiveQuotaScan, len(s.UserHomeScans))
  788. copy(scans, s.UserHomeScans)
  789. return scans
  790. }
  791. // AddUserQuotaScan adds a user to the ones with active quota scans.
  792. // Returns false if the user has a quota scan already running
  793. func (s *ActiveScans) AddUserQuotaScan(username string) bool {
  794. s.Lock()
  795. defer s.Unlock()
  796. for _, scan := range s.UserHomeScans {
  797. if scan.Username == username {
  798. return false
  799. }
  800. }
  801. s.UserHomeScans = append(s.UserHomeScans, ActiveQuotaScan{
  802. Username: username,
  803. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  804. })
  805. return true
  806. }
  807. // RemoveUserQuotaScan removes a user from the ones with active quota scans.
  808. // Returns false if the user has no active quota scans
  809. func (s *ActiveScans) RemoveUserQuotaScan(username string) bool {
  810. s.Lock()
  811. defer s.Unlock()
  812. indexToRemove := -1
  813. for i, scan := range s.UserHomeScans {
  814. if scan.Username == username {
  815. indexToRemove = i
  816. break
  817. }
  818. }
  819. if indexToRemove >= 0 {
  820. s.UserHomeScans[indexToRemove] = s.UserHomeScans[len(s.UserHomeScans)-1]
  821. s.UserHomeScans = s.UserHomeScans[:len(s.UserHomeScans)-1]
  822. return true
  823. }
  824. return false
  825. }
  826. // GetVFoldersQuotaScans returns the active quota scans for virtual folders
  827. func (s *ActiveScans) GetVFoldersQuotaScans() []ActiveVirtualFolderQuotaScan {
  828. s.RLock()
  829. defer s.RUnlock()
  830. scans := make([]ActiveVirtualFolderQuotaScan, len(s.FolderScans))
  831. copy(scans, s.FolderScans)
  832. return scans
  833. }
  834. // AddVFolderQuotaScan adds a virtual folder to the ones with active quota scans.
  835. // Returns false if the folder has a quota scan already running
  836. func (s *ActiveScans) AddVFolderQuotaScan(folderName string) bool {
  837. s.Lock()
  838. defer s.Unlock()
  839. for _, scan := range s.FolderScans {
  840. if scan.Name == folderName {
  841. return false
  842. }
  843. }
  844. s.FolderScans = append(s.FolderScans, ActiveVirtualFolderQuotaScan{
  845. Name: folderName,
  846. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  847. })
  848. return true
  849. }
  850. // RemoveVFolderQuotaScan removes a folder from the ones with active quota scans.
  851. // Returns false if the folder has no active quota scans
  852. func (s *ActiveScans) RemoveVFolderQuotaScan(folderName string) bool {
  853. s.Lock()
  854. defer s.Unlock()
  855. indexToRemove := -1
  856. for i, scan := range s.FolderScans {
  857. if scan.Name == folderName {
  858. indexToRemove = i
  859. break
  860. }
  861. }
  862. if indexToRemove >= 0 {
  863. s.FolderScans[indexToRemove] = s.FolderScans[len(s.FolderScans)-1]
  864. s.FolderScans = s.FolderScans[:len(s.FolderScans)-1]
  865. return true
  866. }
  867. return false
  868. }