common.go 26 KB

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