common.go 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261
  1. // Copyright (C) 2019-2022 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. // Package common defines code shared among file transfer packages and protocols
  15. package common
  16. import (
  17. "context"
  18. "errors"
  19. "fmt"
  20. "net"
  21. "net/http"
  22. "net/url"
  23. "os"
  24. "os/exec"
  25. "path/filepath"
  26. "strconv"
  27. "strings"
  28. "sync"
  29. "sync/atomic"
  30. "time"
  31. "github.com/pires/go-proxyproto"
  32. "github.com/drakkan/sftpgo/v2/internal/command"
  33. "github.com/drakkan/sftpgo/v2/internal/dataprovider"
  34. "github.com/drakkan/sftpgo/v2/internal/httpclient"
  35. "github.com/drakkan/sftpgo/v2/internal/logger"
  36. "github.com/drakkan/sftpgo/v2/internal/metric"
  37. "github.com/drakkan/sftpgo/v2/internal/plugin"
  38. "github.com/drakkan/sftpgo/v2/internal/util"
  39. "github.com/drakkan/sftpgo/v2/internal/vfs"
  40. )
  41. // constants
  42. const (
  43. logSender = "common"
  44. uploadLogSender = "Upload"
  45. downloadLogSender = "Download"
  46. renameLogSender = "Rename"
  47. rmdirLogSender = "Rmdir"
  48. mkdirLogSender = "Mkdir"
  49. symlinkLogSender = "Symlink"
  50. removeLogSender = "Remove"
  51. chownLogSender = "Chown"
  52. chmodLogSender = "Chmod"
  53. chtimesLogSender = "Chtimes"
  54. truncateLogSender = "Truncate"
  55. operationDownload = "download"
  56. operationUpload = "upload"
  57. operationDelete = "delete"
  58. // Pre-download action name
  59. OperationPreDownload = "pre-download"
  60. // Pre-upload action name
  61. OperationPreUpload = "pre-upload"
  62. operationPreDelete = "pre-delete"
  63. operationRename = "rename"
  64. operationMkdir = "mkdir"
  65. operationRmdir = "rmdir"
  66. // SSH command action name
  67. OperationSSHCmd = "ssh_cmd"
  68. chtimesFormat = "2006-01-02T15:04:05" // YYYY-MM-DDTHH:MM:SS
  69. idleTimeoutCheckInterval = 3 * time.Minute
  70. periodicTimeoutCheckInterval = 1 * time.Minute
  71. )
  72. // Stat flags
  73. const (
  74. StatAttrUIDGID = 1
  75. StatAttrPerms = 2
  76. StatAttrTimes = 4
  77. StatAttrSize = 8
  78. )
  79. // Transfer types
  80. const (
  81. TransferUpload = iota
  82. TransferDownload
  83. )
  84. // Supported protocols
  85. const (
  86. ProtocolSFTP = "SFTP"
  87. ProtocolSCP = "SCP"
  88. ProtocolSSH = "SSH"
  89. ProtocolFTP = "FTP"
  90. ProtocolWebDAV = "DAV"
  91. ProtocolHTTP = "HTTP"
  92. ProtocolHTTPShare = "HTTPShare"
  93. ProtocolDataRetention = "DataRetention"
  94. ProtocolOIDC = "OIDC"
  95. )
  96. // Upload modes
  97. const (
  98. UploadModeStandard = iota
  99. UploadModeAtomic
  100. UploadModeAtomicWithResume
  101. )
  102. func init() {
  103. Connections.clients = clientsMap{
  104. clients: make(map[string]int),
  105. }
  106. Connections.perUserConns = make(map[string]int)
  107. }
  108. // errors definitions
  109. var (
  110. ErrPermissionDenied = errors.New("permission denied")
  111. ErrNotExist = errors.New("no such file or directory")
  112. ErrOpUnsupported = errors.New("operation unsupported")
  113. ErrGenericFailure = errors.New("failure")
  114. ErrQuotaExceeded = errors.New("denying write due to space limit")
  115. ErrReadQuotaExceeded = errors.New("denying read due to quota limit")
  116. ErrConnectionDenied = errors.New("you are not allowed to connect")
  117. ErrNoBinding = errors.New("no binding configured")
  118. ErrCrtRevoked = errors.New("your certificate has been revoked")
  119. ErrNoCredentials = errors.New("no credential provided")
  120. ErrInternalFailure = errors.New("internal failure")
  121. ErrTransferAborted = errors.New("transfer aborted")
  122. errNoTransfer = errors.New("requested transfer not found")
  123. errTransferMismatch = errors.New("transfer mismatch")
  124. )
  125. var (
  126. // Config is the configuration for the supported protocols
  127. Config Configuration
  128. // Connections is the list of active connections
  129. Connections ActiveConnections
  130. // QuotaScans is the list of active quota scans
  131. QuotaScans ActiveScans
  132. transfersChecker TransfersChecker
  133. supportedProtocols = []string{ProtocolSFTP, ProtocolSCP, ProtocolSSH, ProtocolFTP, ProtocolWebDAV,
  134. ProtocolHTTP, ProtocolHTTPShare, ProtocolOIDC}
  135. disconnHookProtocols = []string{ProtocolSFTP, ProtocolSCP, ProtocolSSH, ProtocolFTP}
  136. // the map key is the protocol, for each protocol we can have multiple rate limiters
  137. rateLimiters map[string][]*rateLimiter
  138. )
  139. // Initialize sets the common configuration
  140. func Initialize(c Configuration, isShared int) error {
  141. Config = c
  142. Config.Actions.ExecuteOn = util.RemoveDuplicates(Config.Actions.ExecuteOn, true)
  143. Config.Actions.ExecuteSync = util.RemoveDuplicates(Config.Actions.ExecuteSync, true)
  144. Config.ProxyAllowed = util.RemoveDuplicates(Config.ProxyAllowed, true)
  145. Config.idleLoginTimeout = 2 * time.Minute
  146. Config.idleTimeoutAsDuration = time.Duration(Config.IdleTimeout) * time.Minute
  147. startPeriodicChecks(periodicTimeoutCheckInterval)
  148. Config.defender = nil
  149. Config.whitelist = nil
  150. rateLimiters = make(map[string][]*rateLimiter)
  151. for _, rlCfg := range c.RateLimitersConfig {
  152. if rlCfg.isEnabled() {
  153. if err := rlCfg.validate(); err != nil {
  154. return fmt.Errorf("rate limiters initialization error: %w", err)
  155. }
  156. allowList, err := util.ParseAllowedIPAndRanges(rlCfg.AllowList)
  157. if err != nil {
  158. return fmt.Errorf("unable to parse rate limiter allow list %v: %v", rlCfg.AllowList, err)
  159. }
  160. rateLimiter := rlCfg.getLimiter()
  161. rateLimiter.allowList = allowList
  162. for _, protocol := range rlCfg.Protocols {
  163. rateLimiters[protocol] = append(rateLimiters[protocol], rateLimiter)
  164. }
  165. }
  166. }
  167. if c.DefenderConfig.Enabled {
  168. if !util.Contains(supportedDefenderDrivers, c.DefenderConfig.Driver) {
  169. return fmt.Errorf("unsupported defender driver %#v", c.DefenderConfig.Driver)
  170. }
  171. var defender Defender
  172. var err error
  173. switch c.DefenderConfig.Driver {
  174. case DefenderDriverProvider:
  175. defender, err = newDBDefender(&c.DefenderConfig)
  176. default:
  177. defender, err = newInMemoryDefender(&c.DefenderConfig)
  178. }
  179. if err != nil {
  180. return fmt.Errorf("defender initialization error: %v", err)
  181. }
  182. logger.Info(logSender, "", "defender initialized with config %+v", c.DefenderConfig)
  183. Config.defender = defender
  184. }
  185. if c.WhiteListFile != "" {
  186. whitelist := &whitelist{
  187. fileName: c.WhiteListFile,
  188. }
  189. if err := whitelist.reload(); err != nil {
  190. return fmt.Errorf("whitelist initialization error: %w", err)
  191. }
  192. logger.Info(logSender, "", "whitelist initialized from file: %#v", c.WhiteListFile)
  193. Config.whitelist = whitelist
  194. }
  195. vfs.SetTempPath(c.TempPath)
  196. dataprovider.SetTempPath(c.TempPath)
  197. transfersChecker = getTransfersChecker(isShared)
  198. return nil
  199. }
  200. // LimitRate blocks until all the configured rate limiters
  201. // allow one event to happen.
  202. // It returns an error if the time to wait exceeds the max
  203. // allowed delay
  204. func LimitRate(protocol, ip string) (time.Duration, error) {
  205. for _, limiter := range rateLimiters[protocol] {
  206. if delay, err := limiter.Wait(ip); err != nil {
  207. logger.Debug(logSender, "", "protocol %v ip %v: %v", protocol, ip, err)
  208. return delay, err
  209. }
  210. }
  211. return 0, nil
  212. }
  213. // Reload reloads the whitelist, the IP filter plugin and the defender's block and safe lists
  214. func Reload() error {
  215. plugin.Handler.ReloadFilter()
  216. var errWithelist error
  217. if Config.whitelist != nil {
  218. errWithelist = Config.whitelist.reload()
  219. }
  220. if Config.defender == nil {
  221. return errWithelist
  222. }
  223. if err := Config.defender.Reload(); err != nil {
  224. return err
  225. }
  226. return errWithelist
  227. }
  228. // IsBanned returns true if the specified IP address is banned
  229. func IsBanned(ip string) bool {
  230. if plugin.Handler.IsIPBanned(ip) {
  231. return true
  232. }
  233. if Config.defender == nil {
  234. return false
  235. }
  236. return Config.defender.IsBanned(ip)
  237. }
  238. // GetDefenderBanTime returns the ban time for the given IP
  239. // or nil if the IP is not banned or the defender is disabled
  240. func GetDefenderBanTime(ip string) (*time.Time, error) {
  241. if Config.defender == nil {
  242. return nil, nil
  243. }
  244. return Config.defender.GetBanTime(ip)
  245. }
  246. // GetDefenderHosts returns hosts that are banned or for which some violations have been detected
  247. func GetDefenderHosts() ([]dataprovider.DefenderEntry, error) {
  248. if Config.defender == nil {
  249. return nil, nil
  250. }
  251. return Config.defender.GetHosts()
  252. }
  253. // GetDefenderHost returns a defender host by ip, if any
  254. func GetDefenderHost(ip string) (dataprovider.DefenderEntry, error) {
  255. if Config.defender == nil {
  256. return dataprovider.DefenderEntry{}, errors.New("defender is disabled")
  257. }
  258. return Config.defender.GetHost(ip)
  259. }
  260. // DeleteDefenderHost removes the specified IP address from the defender lists
  261. func DeleteDefenderHost(ip string) bool {
  262. if Config.defender == nil {
  263. return false
  264. }
  265. return Config.defender.DeleteHost(ip)
  266. }
  267. // GetDefenderScore returns the score for the given IP
  268. func GetDefenderScore(ip string) (int, error) {
  269. if Config.defender == nil {
  270. return 0, nil
  271. }
  272. return Config.defender.GetScore(ip)
  273. }
  274. // AddDefenderEvent adds the specified defender event for the given IP
  275. func AddDefenderEvent(ip string, event HostEvent) {
  276. if Config.defender == nil {
  277. return
  278. }
  279. Config.defender.AddEvent(ip, event)
  280. }
  281. func startPeriodicChecks(duration time.Duration) {
  282. startEventScheduler()
  283. spec := fmt.Sprintf("@every %s", duration)
  284. _, err := eventScheduler.AddFunc(spec, Connections.checkTransfers)
  285. util.PanicOnError(err)
  286. logger.Info(logSender, "", "scheduled overquota transfers check, schedule %q", spec)
  287. if Config.IdleTimeout > 0 {
  288. ratio := idleTimeoutCheckInterval / periodicTimeoutCheckInterval
  289. spec = fmt.Sprintf("@every %s", duration*ratio)
  290. _, err = eventScheduler.AddFunc(spec, Connections.checkIdles)
  291. util.PanicOnError(err)
  292. logger.Info(logSender, "", "scheduled idle connections check, schedule %q", spec)
  293. }
  294. }
  295. // ActiveTransfer defines the interface for the current active transfers
  296. type ActiveTransfer interface {
  297. GetID() int64
  298. GetType() int
  299. GetSize() int64
  300. GetDownloadedSize() int64
  301. GetUploadedSize() int64
  302. GetVirtualPath() string
  303. GetStartTime() time.Time
  304. SignalClose(err error)
  305. Truncate(fsPath string, size int64) (int64, error)
  306. GetRealFsPath(fsPath string) string
  307. SetTimes(fsPath string, atime time.Time, mtime time.Time) bool
  308. GetTruncatedSize() int64
  309. HasSizeLimit() bool
  310. }
  311. // ActiveConnection defines the interface for the current active connections
  312. type ActiveConnection interface {
  313. GetID() string
  314. GetUsername() string
  315. GetMaxSessions() int
  316. GetLocalAddress() string
  317. GetRemoteAddress() string
  318. GetClientVersion() string
  319. GetProtocol() string
  320. GetConnectionTime() time.Time
  321. GetLastActivity() time.Time
  322. GetCommand() string
  323. Disconnect() error
  324. AddTransfer(t ActiveTransfer)
  325. RemoveTransfer(t ActiveTransfer)
  326. GetTransfers() []ConnectionTransfer
  327. SignalTransferClose(transferID int64, err error)
  328. CloseFS() error
  329. }
  330. // StatAttributes defines the attributes for set stat commands
  331. type StatAttributes struct {
  332. Mode os.FileMode
  333. Atime time.Time
  334. Mtime time.Time
  335. UID int
  336. GID int
  337. Flags int
  338. Size int64
  339. }
  340. // ConnectionTransfer defines the trasfer details to expose
  341. type ConnectionTransfer struct {
  342. ID int64 `json:"-"`
  343. OperationType string `json:"operation_type"`
  344. StartTime int64 `json:"start_time"`
  345. Size int64 `json:"size"`
  346. VirtualPath string `json:"path"`
  347. HasSizeLimit bool `json:"-"`
  348. ULSize int64 `json:"-"`
  349. DLSize int64 `json:"-"`
  350. }
  351. func (t *ConnectionTransfer) getConnectionTransferAsString() string {
  352. result := ""
  353. switch t.OperationType {
  354. case operationUpload:
  355. result += "UL "
  356. case operationDownload:
  357. result += "DL "
  358. }
  359. result += fmt.Sprintf("%q ", t.VirtualPath)
  360. if t.Size > 0 {
  361. elapsed := time.Since(util.GetTimeFromMsecSinceEpoch(t.StartTime))
  362. speed := float64(t.Size) / float64(util.GetTimeAsMsSinceEpoch(time.Now())-t.StartTime)
  363. result += fmt.Sprintf("Size: %s Elapsed: %s Speed: \"%.1f KB/s\"", util.ByteCountIEC(t.Size),
  364. util.GetDurationAsString(elapsed), speed)
  365. }
  366. return result
  367. }
  368. type whitelist struct {
  369. fileName string
  370. sync.RWMutex
  371. list HostList
  372. }
  373. func (l *whitelist) reload() error {
  374. list, err := loadHostListFromFile(l.fileName)
  375. if err != nil {
  376. return err
  377. }
  378. if list == nil {
  379. return errors.New("cannot accept a nil whitelist")
  380. }
  381. l.Lock()
  382. defer l.Unlock()
  383. l.list = *list
  384. return nil
  385. }
  386. func (l *whitelist) isAllowed(ip string) bool {
  387. l.RLock()
  388. defer l.RUnlock()
  389. return l.list.isListed(ip)
  390. }
  391. // Configuration defines configuration parameters common to all supported protocols
  392. type Configuration struct {
  393. // Maximum idle timeout as minutes. If a client is idle for a time that exceeds this setting it will be disconnected.
  394. // 0 means disabled
  395. IdleTimeout int `json:"idle_timeout" mapstructure:"idle_timeout"`
  396. // UploadMode 0 means standard, the files are uploaded directly to the requested path.
  397. // 1 means atomic: the files are uploaded to a temporary path and renamed to the requested path
  398. // when the client ends the upload. Atomic mode avoid problems such as a web server that
  399. // serves partial files when the files are being uploaded.
  400. // In atomic mode if there is an upload error the temporary file is deleted and so the requested
  401. // upload path will not contain a partial file.
  402. // 2 means atomic with resume support: as atomic but if there is an upload error the temporary
  403. // file is renamed to the requested path and not deleted, this way a client can reconnect and resume
  404. // the upload.
  405. UploadMode int `json:"upload_mode" mapstructure:"upload_mode"`
  406. // Actions to execute for SFTP file operations and SSH commands
  407. Actions ProtocolActions `json:"actions" mapstructure:"actions"`
  408. // SetstatMode 0 means "normal mode": requests for changing permissions and owner/group are executed.
  409. // 1 means "ignore mode": requests for changing permissions and owner/group are silently ignored.
  410. // 2 means "ignore mode for cloud fs": requests for changing permissions and owner/group are
  411. // silently ignored for cloud based filesystem such as S3, GCS, Azure Blob. Requests for changing
  412. // modification times are ignored for cloud based filesystem if they are not supported.
  413. SetstatMode int `json:"setstat_mode" mapstructure:"setstat_mode"`
  414. // TempPath defines the path for temporary files such as those used for atomic uploads or file pipes.
  415. // If you set this option you must make sure that the defined path exists, is accessible for writing
  416. // by the user running SFTPGo, and is on the same filesystem as the users home directories otherwise
  417. // the renaming for atomic uploads will become a copy and therefore may take a long time.
  418. // The temporary files are not namespaced. The default is generally fine. Leave empty for the default.
  419. TempPath string `json:"temp_path" mapstructure:"temp_path"`
  420. // Support for HAProxy PROXY protocol.
  421. // If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGNIX, you can enable
  422. // the proxy protocol. It provides a convenient way to safely transport connection information
  423. // such as a client's address across multiple layers of NAT or TCP proxies to get the real
  424. // client IP address instead of the proxy IP. Both protocol versions 1 and 2 are supported.
  425. // - 0 means disabled
  426. // - 1 means proxy protocol enabled. Proxy header will be used and requests without proxy header will be accepted.
  427. // - 2 means proxy protocol required. Proxy header will be used and requests without proxy header will be rejected.
  428. // If the proxy protocol is enabled in SFTPGo then you have to enable the protocol in your proxy configuration too,
  429. // for example for HAProxy add "send-proxy" or "send-proxy-v2" to each server configuration line.
  430. ProxyProtocol int `json:"proxy_protocol" mapstructure:"proxy_protocol"`
  431. // List of IP addresses and IP ranges allowed to send the proxy header.
  432. // If proxy protocol is set to 1 and we receive a proxy header from an IP that is not in the list then the
  433. // connection will be accepted and the header will be ignored.
  434. // If proxy protocol is set to 2 and we receive a proxy header from an IP that is not in the list then the
  435. // connection will be rejected.
  436. ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
  437. // Absolute path to an external program or an HTTP URL to invoke as soon as SFTPGo starts.
  438. // If you define an HTTP URL it will be invoked using a `GET` request.
  439. // Please note that SFTPGo services may not yet be available when this hook is run.
  440. // Leave empty do disable.
  441. StartupHook string `json:"startup_hook" mapstructure:"startup_hook"`
  442. // Absolute path to an external program or an HTTP URL to invoke after a user connects
  443. // and before he tries to login. It allows you to reject the connection based on the source
  444. // ip address. Leave empty do disable.
  445. PostConnectHook string `json:"post_connect_hook" mapstructure:"post_connect_hook"`
  446. // Absolute path to an external program or an HTTP URL to invoke after an SSH/FTP connection ends.
  447. // Leave empty do disable.
  448. PostDisconnectHook string `json:"post_disconnect_hook" mapstructure:"post_disconnect_hook"`
  449. // Absolute path to an external program or an HTTP URL to invoke after a data retention check completes.
  450. // Leave empty do disable.
  451. DataRetentionHook string `json:"data_retention_hook" mapstructure:"data_retention_hook"`
  452. // Maximum number of concurrent client connections. 0 means unlimited
  453. MaxTotalConnections int `json:"max_total_connections" mapstructure:"max_total_connections"`
  454. // Maximum number of concurrent client connections from the same host (IP). 0 means unlimited
  455. MaxPerHostConnections int `json:"max_per_host_connections" mapstructure:"max_per_host_connections"`
  456. // Path to a file containing a list of IP addresses and/or networks to allow.
  457. // Only the listed IPs/networks can access the configured services, all other client connections
  458. // will be dropped before they even try to authenticate.
  459. WhiteListFile string `json:"whitelist_file" mapstructure:"whitelist_file"`
  460. // Defender configuration
  461. DefenderConfig DefenderConfig `json:"defender" mapstructure:"defender"`
  462. // Rate limiter configurations
  463. RateLimitersConfig []RateLimiterConfig `json:"rate_limiters" mapstructure:"rate_limiters"`
  464. idleTimeoutAsDuration time.Duration
  465. idleLoginTimeout time.Duration
  466. defender Defender
  467. whitelist *whitelist
  468. }
  469. // IsAtomicUploadEnabled returns true if atomic upload is enabled
  470. func (c *Configuration) IsAtomicUploadEnabled() bool {
  471. return c.UploadMode == UploadModeAtomic || c.UploadMode == UploadModeAtomicWithResume
  472. }
  473. // GetProxyListener returns a wrapper for the given listener that supports the
  474. // HAProxy Proxy Protocol
  475. func (c *Configuration) GetProxyListener(listener net.Listener) (*proxyproto.Listener, error) {
  476. var err error
  477. if c.ProxyProtocol > 0 {
  478. var policyFunc func(upstream net.Addr) (proxyproto.Policy, error)
  479. if c.ProxyProtocol == 1 && len(c.ProxyAllowed) > 0 {
  480. policyFunc, err = proxyproto.LaxWhiteListPolicy(c.ProxyAllowed)
  481. if err != nil {
  482. return nil, err
  483. }
  484. }
  485. if c.ProxyProtocol == 2 {
  486. if len(c.ProxyAllowed) == 0 {
  487. policyFunc = func(upstream net.Addr) (proxyproto.Policy, error) {
  488. return proxyproto.REQUIRE, nil
  489. }
  490. } else {
  491. policyFunc, err = proxyproto.StrictWhiteListPolicy(c.ProxyAllowed)
  492. if err != nil {
  493. return nil, err
  494. }
  495. }
  496. }
  497. return &proxyproto.Listener{
  498. Listener: listener,
  499. Policy: policyFunc,
  500. ReadHeaderTimeout: 10 * time.Second,
  501. }, nil
  502. }
  503. return nil, errors.New("proxy protocol not configured")
  504. }
  505. // ExecuteStartupHook runs the startup hook if defined
  506. func (c *Configuration) ExecuteStartupHook() error {
  507. if c.StartupHook == "" {
  508. return nil
  509. }
  510. if strings.HasPrefix(c.StartupHook, "http") {
  511. var url *url.URL
  512. url, err := url.Parse(c.StartupHook)
  513. if err != nil {
  514. logger.Warn(logSender, "", "Invalid startup hook %#v: %v", c.StartupHook, err)
  515. return err
  516. }
  517. startTime := time.Now()
  518. resp, err := httpclient.RetryableGet(url.String())
  519. if err != nil {
  520. logger.Warn(logSender, "", "Error executing startup hook: %v", err)
  521. return err
  522. }
  523. defer resp.Body.Close()
  524. logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, response code: %v", time.Since(startTime), resp.StatusCode)
  525. return nil
  526. }
  527. if !filepath.IsAbs(c.StartupHook) {
  528. err := fmt.Errorf("invalid startup hook %#v", c.StartupHook)
  529. logger.Warn(logSender, "", "Invalid startup hook %#v", c.StartupHook)
  530. return err
  531. }
  532. startTime := time.Now()
  533. timeout, env := command.GetConfig(c.StartupHook)
  534. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  535. defer cancel()
  536. cmd := exec.CommandContext(ctx, c.StartupHook)
  537. cmd.Env = env
  538. err := cmd.Run()
  539. logger.Debug(logSender, "", "Startup hook executed, elapsed: %v, error: %v", time.Since(startTime), err)
  540. return nil
  541. }
  542. func (c *Configuration) executePostDisconnectHook(remoteAddr, protocol, username, connID string, connectionTime time.Time) {
  543. ipAddr := util.GetIPFromRemoteAddress(remoteAddr)
  544. connDuration := int64(time.Since(connectionTime) / time.Millisecond)
  545. if strings.HasPrefix(c.PostDisconnectHook, "http") {
  546. var url *url.URL
  547. url, err := url.Parse(c.PostDisconnectHook)
  548. if err != nil {
  549. logger.Warn(protocol, connID, "Invalid post disconnect hook %#v: %v", c.PostDisconnectHook, err)
  550. return
  551. }
  552. q := url.Query()
  553. q.Add("ip", ipAddr)
  554. q.Add("protocol", protocol)
  555. q.Add("username", username)
  556. q.Add("connection_duration", strconv.FormatInt(connDuration, 10))
  557. url.RawQuery = q.Encode()
  558. startTime := time.Now()
  559. resp, err := httpclient.RetryableGet(url.String())
  560. respCode := 0
  561. if err == nil {
  562. respCode = resp.StatusCode
  563. resp.Body.Close()
  564. }
  565. logger.Debug(protocol, connID, "Post disconnect hook response code: %v, elapsed: %v, err: %v",
  566. respCode, time.Since(startTime), err)
  567. return
  568. }
  569. if !filepath.IsAbs(c.PostDisconnectHook) {
  570. logger.Debug(protocol, connID, "invalid post disconnect hook %#v", c.PostDisconnectHook)
  571. return
  572. }
  573. timeout, env := command.GetConfig(c.PostDisconnectHook)
  574. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  575. defer cancel()
  576. startTime := time.Now()
  577. cmd := exec.CommandContext(ctx, c.PostDisconnectHook)
  578. cmd.Env = append(env,
  579. fmt.Sprintf("SFTPGO_CONNECTION_IP=%v", ipAddr),
  580. fmt.Sprintf("SFTPGO_CONNECTION_USERNAME=%v", username),
  581. fmt.Sprintf("SFTPGO_CONNECTION_DURATION=%v", connDuration),
  582. fmt.Sprintf("SFTPGO_CONNECTION_PROTOCOL=%v", protocol))
  583. err := cmd.Run()
  584. logger.Debug(protocol, connID, "Post disconnect hook executed, elapsed: %v error: %v", time.Since(startTime), err)
  585. }
  586. func (c *Configuration) checkPostDisconnectHook(remoteAddr, protocol, username, connID string, connectionTime time.Time) {
  587. if c.PostDisconnectHook == "" {
  588. return
  589. }
  590. if !util.Contains(disconnHookProtocols, protocol) {
  591. return
  592. }
  593. go c.executePostDisconnectHook(remoteAddr, protocol, username, connID, connectionTime)
  594. }
  595. // ExecutePostConnectHook executes the post connect hook if defined
  596. func (c *Configuration) ExecutePostConnectHook(ipAddr, protocol string) error {
  597. if c.PostConnectHook == "" {
  598. return nil
  599. }
  600. if strings.HasPrefix(c.PostConnectHook, "http") {
  601. var url *url.URL
  602. url, err := url.Parse(c.PostConnectHook)
  603. if err != nil {
  604. logger.Warn(protocol, "", "Login from ip %#v denied, invalid post connect hook %#v: %v",
  605. ipAddr, c.PostConnectHook, err)
  606. return err
  607. }
  608. q := url.Query()
  609. q.Add("ip", ipAddr)
  610. q.Add("protocol", protocol)
  611. url.RawQuery = q.Encode()
  612. resp, err := httpclient.RetryableGet(url.String())
  613. if err != nil {
  614. logger.Warn(protocol, "", "Login from ip %#v denied, error executing post connect hook: %v", ipAddr, err)
  615. return err
  616. }
  617. defer resp.Body.Close()
  618. if resp.StatusCode != http.StatusOK {
  619. logger.Warn(protocol, "", "Login from ip %#v denied, post connect hook response code: %v", ipAddr, resp.StatusCode)
  620. return errUnexpectedHTTResponse
  621. }
  622. return nil
  623. }
  624. if !filepath.IsAbs(c.PostConnectHook) {
  625. err := fmt.Errorf("invalid post connect hook %#v", c.PostConnectHook)
  626. logger.Warn(protocol, "", "Login from ip %#v denied: %v", ipAddr, err)
  627. return err
  628. }
  629. timeout, env := command.GetConfig(c.PostConnectHook)
  630. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  631. defer cancel()
  632. cmd := exec.CommandContext(ctx, c.PostConnectHook)
  633. cmd.Env = append(env,
  634. fmt.Sprintf("SFTPGO_CONNECTION_IP=%v", ipAddr),
  635. fmt.Sprintf("SFTPGO_CONNECTION_PROTOCOL=%v", protocol))
  636. err := cmd.Run()
  637. if err != nil {
  638. logger.Warn(protocol, "", "Login from ip %#v denied, connect hook error: %v", ipAddr, err)
  639. }
  640. return err
  641. }
  642. // SSHConnection defines an ssh connection.
  643. // Each SSH connection can open several channels for SFTP or SSH commands
  644. type SSHConnection struct {
  645. id string
  646. conn net.Conn
  647. lastActivity int64
  648. }
  649. // NewSSHConnection returns a new SSHConnection
  650. func NewSSHConnection(id string, conn net.Conn) *SSHConnection {
  651. return &SSHConnection{
  652. id: id,
  653. conn: conn,
  654. lastActivity: time.Now().UnixNano(),
  655. }
  656. }
  657. // GetID returns the ID for this SSHConnection
  658. func (c *SSHConnection) GetID() string {
  659. return c.id
  660. }
  661. // UpdateLastActivity updates last activity for this connection
  662. func (c *SSHConnection) UpdateLastActivity() {
  663. atomic.StoreInt64(&c.lastActivity, time.Now().UnixNano())
  664. }
  665. // GetLastActivity returns the last connection activity
  666. func (c *SSHConnection) GetLastActivity() time.Time {
  667. return time.Unix(0, atomic.LoadInt64(&c.lastActivity))
  668. }
  669. // Close closes the underlying network connection
  670. func (c *SSHConnection) Close() error {
  671. return c.conn.Close()
  672. }
  673. // ActiveConnections holds the currect active connections with the associated transfers
  674. type ActiveConnections struct {
  675. // clients contains both authenticated and estabilished connections and the ones waiting
  676. // for authentication
  677. clients clientsMap
  678. transfersCheckStatus int32
  679. sync.RWMutex
  680. connections []ActiveConnection
  681. sshConnections []*SSHConnection
  682. perUserConns map[string]int
  683. }
  684. // internal method, must be called within a locked block
  685. func (conns *ActiveConnections) addUserConnection(username string) {
  686. if username == "" {
  687. return
  688. }
  689. conns.perUserConns[username]++
  690. }
  691. // internal method, must be called within a locked block
  692. func (conns *ActiveConnections) removeUserConnection(username string) {
  693. if username == "" {
  694. return
  695. }
  696. if val, ok := conns.perUserConns[username]; ok {
  697. conns.perUserConns[username]--
  698. if val > 1 {
  699. return
  700. }
  701. delete(conns.perUserConns, username)
  702. }
  703. }
  704. // GetActiveSessions returns the number of active sessions for the given username.
  705. // We return the open sessions for any protocol
  706. func (conns *ActiveConnections) GetActiveSessions(username string) int {
  707. conns.RLock()
  708. defer conns.RUnlock()
  709. return conns.perUserConns[username]
  710. }
  711. // Add adds a new connection to the active ones
  712. func (conns *ActiveConnections) Add(c ActiveConnection) error {
  713. conns.Lock()
  714. defer conns.Unlock()
  715. if username := c.GetUsername(); username != "" {
  716. if maxSessions := c.GetMaxSessions(); maxSessions > 0 {
  717. if val := conns.perUserConns[username]; val >= maxSessions {
  718. return fmt.Errorf("too many open sessions: %d/%d", val, maxSessions)
  719. }
  720. }
  721. conns.addUserConnection(username)
  722. }
  723. conns.connections = append(conns.connections, c)
  724. metric.UpdateActiveConnectionsSize(len(conns.connections))
  725. logger.Debug(c.GetProtocol(), c.GetID(), "connection added, local address %#v, remote address %#v, num open connections: %v",
  726. c.GetLocalAddress(), c.GetRemoteAddress(), len(conns.connections))
  727. return nil
  728. }
  729. // Swap replaces an existing connection with the given one.
  730. // This method is useful if you have to change some connection details
  731. // for example for FTP is used to update the connection once the user
  732. // authenticates
  733. func (conns *ActiveConnections) Swap(c ActiveConnection) error {
  734. conns.Lock()
  735. defer conns.Unlock()
  736. for idx, conn := range conns.connections {
  737. if conn.GetID() == c.GetID() {
  738. conns.removeUserConnection(conn.GetUsername())
  739. if username := c.GetUsername(); username != "" {
  740. if maxSessions := c.GetMaxSessions(); maxSessions > 0 {
  741. if val := conns.perUserConns[username]; val >= maxSessions {
  742. conns.addUserConnection(conn.GetUsername())
  743. return fmt.Errorf("too many open sessions: %d/%d", val, maxSessions)
  744. }
  745. }
  746. conns.addUserConnection(username)
  747. }
  748. err := conn.CloseFS()
  749. conns.connections[idx] = c
  750. logger.Debug(logSender, c.GetID(), "connection swapped, close fs error: %v", err)
  751. conn = nil
  752. return nil
  753. }
  754. }
  755. return errors.New("connection to swap not found")
  756. }
  757. // Remove removes a connection from the active ones
  758. func (conns *ActiveConnections) Remove(connectionID string) {
  759. conns.Lock()
  760. defer conns.Unlock()
  761. for idx, conn := range conns.connections {
  762. if conn.GetID() == connectionID {
  763. err := conn.CloseFS()
  764. lastIdx := len(conns.connections) - 1
  765. conns.connections[idx] = conns.connections[lastIdx]
  766. conns.connections[lastIdx] = nil
  767. conns.connections = conns.connections[:lastIdx]
  768. conns.removeUserConnection(conn.GetUsername())
  769. metric.UpdateActiveConnectionsSize(lastIdx)
  770. logger.Debug(conn.GetProtocol(), conn.GetID(), "connection removed, local address %#v, remote address %#v close fs error: %v, num open connections: %v",
  771. conn.GetLocalAddress(), conn.GetRemoteAddress(), err, lastIdx)
  772. Config.checkPostDisconnectHook(conn.GetRemoteAddress(), conn.GetProtocol(), conn.GetUsername(),
  773. conn.GetID(), conn.GetConnectionTime())
  774. return
  775. }
  776. }
  777. logger.Warn(logSender, "", "connection id %#v to remove not found!", connectionID)
  778. }
  779. // Close closes an active connection.
  780. // It returns true on success
  781. func (conns *ActiveConnections) Close(connectionID string) bool {
  782. conns.RLock()
  783. result := false
  784. for _, c := range conns.connections {
  785. if c.GetID() == connectionID {
  786. defer func(conn ActiveConnection) {
  787. err := conn.Disconnect()
  788. logger.Debug(conn.GetProtocol(), conn.GetID(), "close connection requested, close err: %v", err)
  789. }(c)
  790. result = true
  791. break
  792. }
  793. }
  794. conns.RUnlock()
  795. return result
  796. }
  797. // AddSSHConnection adds a new ssh connection to the active ones
  798. func (conns *ActiveConnections) AddSSHConnection(c *SSHConnection) {
  799. conns.Lock()
  800. defer conns.Unlock()
  801. conns.sshConnections = append(conns.sshConnections, c)
  802. logger.Debug(logSender, c.GetID(), "ssh connection added, num open connections: %v", len(conns.sshConnections))
  803. }
  804. // RemoveSSHConnection removes a connection from the active ones
  805. func (conns *ActiveConnections) RemoveSSHConnection(connectionID string) {
  806. conns.Lock()
  807. defer conns.Unlock()
  808. for idx, conn := range conns.sshConnections {
  809. if conn.GetID() == connectionID {
  810. lastIdx := len(conns.sshConnections) - 1
  811. conns.sshConnections[idx] = conns.sshConnections[lastIdx]
  812. conns.sshConnections[lastIdx] = nil
  813. conns.sshConnections = conns.sshConnections[:lastIdx]
  814. logger.Debug(logSender, conn.GetID(), "ssh connection removed, num open ssh connections: %v", lastIdx)
  815. return
  816. }
  817. }
  818. logger.Warn(logSender, "", "ssh connection to remove with id %#v not found!", connectionID)
  819. }
  820. func (conns *ActiveConnections) checkIdles() {
  821. conns.RLock()
  822. for _, sshConn := range conns.sshConnections {
  823. idleTime := time.Since(sshConn.GetLastActivity())
  824. if idleTime > Config.idleTimeoutAsDuration {
  825. // we close an SSH connection if it has no active connections associated
  826. idToMatch := fmt.Sprintf("_%s_", sshConn.GetID())
  827. toClose := true
  828. for _, conn := range conns.connections {
  829. if strings.Contains(conn.GetID(), idToMatch) {
  830. if time.Since(conn.GetLastActivity()) <= Config.idleTimeoutAsDuration {
  831. toClose = false
  832. break
  833. }
  834. }
  835. }
  836. if toClose {
  837. defer func(c *SSHConnection) {
  838. err := c.Close()
  839. logger.Debug(logSender, c.GetID(), "close idle SSH connection, idle time: %v, close err: %v",
  840. time.Since(c.GetLastActivity()), err)
  841. }(sshConn)
  842. }
  843. }
  844. }
  845. for _, c := range conns.connections {
  846. idleTime := time.Since(c.GetLastActivity())
  847. isUnauthenticatedFTPUser := (c.GetProtocol() == ProtocolFTP && c.GetUsername() == "")
  848. if idleTime > Config.idleTimeoutAsDuration || (isUnauthenticatedFTPUser && idleTime > Config.idleLoginTimeout) {
  849. defer func(conn ActiveConnection, isFTPNoAuth bool) {
  850. err := conn.Disconnect()
  851. logger.Debug(conn.GetProtocol(), conn.GetID(), "close idle connection, idle time: %v, username: %#v close err: %v",
  852. time.Since(conn.GetLastActivity()), conn.GetUsername(), err)
  853. if isFTPNoAuth {
  854. ip := util.GetIPFromRemoteAddress(c.GetRemoteAddress())
  855. logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTryed, c.GetProtocol(), "client idle")
  856. metric.AddNoAuthTryed()
  857. AddDefenderEvent(ip, HostEventNoLoginTried)
  858. dataprovider.ExecutePostLoginHook(&dataprovider.User{}, dataprovider.LoginMethodNoAuthTryed, ip, c.GetProtocol(),
  859. dataprovider.ErrNoAuthTryed)
  860. }
  861. }(c, isUnauthenticatedFTPUser)
  862. }
  863. }
  864. conns.RUnlock()
  865. }
  866. func (conns *ActiveConnections) checkTransfers() {
  867. if atomic.LoadInt32(&conns.transfersCheckStatus) == 1 {
  868. logger.Warn(logSender, "", "the previous transfer check is still running, skipping execution")
  869. return
  870. }
  871. atomic.StoreInt32(&conns.transfersCheckStatus, 1)
  872. defer atomic.StoreInt32(&conns.transfersCheckStatus, 0)
  873. conns.RLock()
  874. if len(conns.connections) < 2 {
  875. conns.RUnlock()
  876. return
  877. }
  878. var wg sync.WaitGroup
  879. logger.Debug(logSender, "", "start concurrent transfers check")
  880. // update the current size for transfers to monitors
  881. for _, c := range conns.connections {
  882. for _, t := range c.GetTransfers() {
  883. if t.HasSizeLimit {
  884. wg.Add(1)
  885. go func(transfer ConnectionTransfer, connID string) {
  886. defer wg.Done()
  887. transfersChecker.UpdateTransferCurrentSizes(transfer.ULSize, transfer.DLSize, transfer.ID, connID)
  888. }(t, c.GetID())
  889. }
  890. }
  891. }
  892. conns.RUnlock()
  893. logger.Debug(logSender, "", "waiting for the update of the transfers current size")
  894. wg.Wait()
  895. logger.Debug(logSender, "", "getting overquota transfers")
  896. overquotaTransfers := transfersChecker.GetOverquotaTransfers()
  897. logger.Debug(logSender, "", "number of overquota transfers: %v", len(overquotaTransfers))
  898. if len(overquotaTransfers) == 0 {
  899. return
  900. }
  901. conns.RLock()
  902. defer conns.RUnlock()
  903. for _, c := range conns.connections {
  904. for _, overquotaTransfer := range overquotaTransfers {
  905. if c.GetID() == overquotaTransfer.ConnID {
  906. logger.Info(logSender, c.GetID(), "user %#v is overquota, try to close transfer id %v",
  907. c.GetUsername(), overquotaTransfer.TransferID)
  908. var err error
  909. if overquotaTransfer.TransferType == TransferDownload {
  910. err = getReadQuotaExceededError(c.GetProtocol())
  911. } else {
  912. err = getQuotaExceededError(c.GetProtocol())
  913. }
  914. c.SignalTransferClose(overquotaTransfer.TransferID, err)
  915. }
  916. }
  917. }
  918. logger.Debug(logSender, "", "transfers check completed")
  919. }
  920. // AddClientConnection stores a new client connection
  921. func (conns *ActiveConnections) AddClientConnection(ipAddr string) {
  922. conns.clients.add(ipAddr)
  923. }
  924. // RemoveClientConnection removes a disconnected client from the tracked ones
  925. func (conns *ActiveConnections) RemoveClientConnection(ipAddr string) {
  926. conns.clients.remove(ipAddr)
  927. }
  928. // GetClientConnections returns the total number of client connections
  929. func (conns *ActiveConnections) GetClientConnections() int32 {
  930. return conns.clients.getTotal()
  931. }
  932. // IsNewConnectionAllowed returns false if the maximum number of concurrent allowed connections is exceeded
  933. // or a whitelist is defined and the specified ipAddr is not listed
  934. func (conns *ActiveConnections) IsNewConnectionAllowed(ipAddr string) bool {
  935. if Config.whitelist != nil {
  936. if !Config.whitelist.isAllowed(ipAddr) {
  937. return false
  938. }
  939. }
  940. if Config.MaxTotalConnections == 0 && Config.MaxPerHostConnections == 0 {
  941. return true
  942. }
  943. if Config.MaxPerHostConnections > 0 {
  944. if total := conns.clients.getTotalFrom(ipAddr); total > Config.MaxPerHostConnections {
  945. logger.Debug(logSender, "", "active connections from %v %v/%v", ipAddr, total, Config.MaxPerHostConnections)
  946. AddDefenderEvent(ipAddr, HostEventLimitExceeded)
  947. return false
  948. }
  949. }
  950. if Config.MaxTotalConnections > 0 {
  951. if total := conns.clients.getTotal(); total > int32(Config.MaxTotalConnections) {
  952. logger.Debug(logSender, "", "active client connections %v/%v", total, Config.MaxTotalConnections)
  953. return false
  954. }
  955. // on a single SFTP connection we could have multiple SFTP channels or commands
  956. // so we check the estabilished connections too
  957. conns.RLock()
  958. defer conns.RUnlock()
  959. return len(conns.connections) < Config.MaxTotalConnections
  960. }
  961. return true
  962. }
  963. // GetStats returns stats for active connections
  964. func (conns *ActiveConnections) GetStats() []ConnectionStatus {
  965. conns.RLock()
  966. defer conns.RUnlock()
  967. stats := make([]ConnectionStatus, 0, len(conns.connections))
  968. for _, c := range conns.connections {
  969. stat := ConnectionStatus{
  970. Username: c.GetUsername(),
  971. ConnectionID: c.GetID(),
  972. ClientVersion: c.GetClientVersion(),
  973. RemoteAddress: c.GetRemoteAddress(),
  974. ConnectionTime: util.GetTimeAsMsSinceEpoch(c.GetConnectionTime()),
  975. LastActivity: util.GetTimeAsMsSinceEpoch(c.GetLastActivity()),
  976. Protocol: c.GetProtocol(),
  977. Command: c.GetCommand(),
  978. Transfers: c.GetTransfers(),
  979. }
  980. stats = append(stats, stat)
  981. }
  982. return stats
  983. }
  984. // ConnectionStatus returns the status for an active connection
  985. type ConnectionStatus struct {
  986. // Logged in username
  987. Username string `json:"username"`
  988. // Unique identifier for the connection
  989. ConnectionID string `json:"connection_id"`
  990. // client's version string
  991. ClientVersion string `json:"client_version,omitempty"`
  992. // Remote address for this connection
  993. RemoteAddress string `json:"remote_address"`
  994. // Connection time as unix timestamp in milliseconds
  995. ConnectionTime int64 `json:"connection_time"`
  996. // Last activity as unix timestamp in milliseconds
  997. LastActivity int64 `json:"last_activity"`
  998. // Protocol for this connection
  999. Protocol string `json:"protocol"`
  1000. // active uploads/downloads
  1001. Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
  1002. // SSH command or WebDAV method
  1003. Command string `json:"command,omitempty"`
  1004. }
  1005. // GetConnectionDuration returns the connection duration as string
  1006. func (c *ConnectionStatus) GetConnectionDuration() string {
  1007. elapsed := time.Since(util.GetTimeFromMsecSinceEpoch(c.ConnectionTime))
  1008. return util.GetDurationAsString(elapsed)
  1009. }
  1010. // GetConnectionInfo returns connection info.
  1011. // Protocol,Client Version and RemoteAddress are returned.
  1012. func (c *ConnectionStatus) GetConnectionInfo() string {
  1013. var result strings.Builder
  1014. result.WriteString(fmt.Sprintf("%v. Client: %#v From: %#v", c.Protocol, c.ClientVersion, c.RemoteAddress))
  1015. if c.Command == "" {
  1016. return result.String()
  1017. }
  1018. switch c.Protocol {
  1019. case ProtocolSSH, ProtocolFTP:
  1020. result.WriteString(fmt.Sprintf(". Command: %#v", c.Command))
  1021. case ProtocolWebDAV:
  1022. result.WriteString(fmt.Sprintf(". Method: %#v", c.Command))
  1023. }
  1024. return result.String()
  1025. }
  1026. // GetTransfersAsString returns the active transfers as string
  1027. func (c *ConnectionStatus) GetTransfersAsString() string {
  1028. result := ""
  1029. for _, t := range c.Transfers {
  1030. if result != "" {
  1031. result += ". "
  1032. }
  1033. result += t.getConnectionTransferAsString()
  1034. }
  1035. return result
  1036. }
  1037. // ActiveQuotaScan defines an active quota scan for a user home dir
  1038. type ActiveQuotaScan struct {
  1039. // Username to which the quota scan refers
  1040. Username string `json:"username"`
  1041. // quota scan start time as unix timestamp in milliseconds
  1042. StartTime int64 `json:"start_time"`
  1043. }
  1044. // ActiveVirtualFolderQuotaScan defines an active quota scan for a virtual folder
  1045. type ActiveVirtualFolderQuotaScan struct {
  1046. // folder name to which the quota scan refers
  1047. Name string `json:"name"`
  1048. // quota scan start time as unix timestamp in milliseconds
  1049. StartTime int64 `json:"start_time"`
  1050. }
  1051. // ActiveScans holds the active quota scans
  1052. type ActiveScans struct {
  1053. sync.RWMutex
  1054. UserScans []ActiveQuotaScan
  1055. FolderScans []ActiveVirtualFolderQuotaScan
  1056. }
  1057. // GetUsersQuotaScans returns the active quota scans for users home directories
  1058. func (s *ActiveScans) GetUsersQuotaScans() []ActiveQuotaScan {
  1059. s.RLock()
  1060. defer s.RUnlock()
  1061. scans := make([]ActiveQuotaScan, len(s.UserScans))
  1062. copy(scans, s.UserScans)
  1063. return scans
  1064. }
  1065. // AddUserQuotaScan adds a user to the ones with active quota scans.
  1066. // Returns false if the user has a quota scan already running
  1067. func (s *ActiveScans) AddUserQuotaScan(username string) bool {
  1068. s.Lock()
  1069. defer s.Unlock()
  1070. for _, scan := range s.UserScans {
  1071. if scan.Username == username {
  1072. return false
  1073. }
  1074. }
  1075. s.UserScans = append(s.UserScans, ActiveQuotaScan{
  1076. Username: username,
  1077. StartTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  1078. })
  1079. return true
  1080. }
  1081. // RemoveUserQuotaScan removes a user from the ones with active quota scans.
  1082. // Returns false if the user has no active quota scans
  1083. func (s *ActiveScans) RemoveUserQuotaScan(username string) bool {
  1084. s.Lock()
  1085. defer s.Unlock()
  1086. for idx, scan := range s.UserScans {
  1087. if scan.Username == username {
  1088. lastIdx := len(s.UserScans) - 1
  1089. s.UserScans[idx] = s.UserScans[lastIdx]
  1090. s.UserScans = s.UserScans[:lastIdx]
  1091. return true
  1092. }
  1093. }
  1094. return false
  1095. }
  1096. // GetVFoldersQuotaScans returns the active quota scans for virtual folders
  1097. func (s *ActiveScans) GetVFoldersQuotaScans() []ActiveVirtualFolderQuotaScan {
  1098. s.RLock()
  1099. defer s.RUnlock()
  1100. scans := make([]ActiveVirtualFolderQuotaScan, len(s.FolderScans))
  1101. copy(scans, s.FolderScans)
  1102. return scans
  1103. }
  1104. // AddVFolderQuotaScan adds a virtual folder to the ones with active quota scans.
  1105. // Returns false if the folder has a quota scan already running
  1106. func (s *ActiveScans) AddVFolderQuotaScan(folderName string) bool {
  1107. s.Lock()
  1108. defer s.Unlock()
  1109. for _, scan := range s.FolderScans {
  1110. if scan.Name == folderName {
  1111. return false
  1112. }
  1113. }
  1114. s.FolderScans = append(s.FolderScans, ActiveVirtualFolderQuotaScan{
  1115. Name: folderName,
  1116. StartTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  1117. })
  1118. return true
  1119. }
  1120. // RemoveVFolderQuotaScan removes a folder from the ones with active quota scans.
  1121. // Returns false if the folder has no active quota scans
  1122. func (s *ActiveScans) RemoveVFolderQuotaScan(folderName string) bool {
  1123. s.Lock()
  1124. defer s.Unlock()
  1125. for idx, scan := range s.FolderScans {
  1126. if scan.Name == folderName {
  1127. lastIdx := len(s.FolderScans) - 1
  1128. s.FolderScans[idx] = s.FolderScans[lastIdx]
  1129. s.FolderScans = s.FolderScans[:lastIdx]
  1130. return true
  1131. }
  1132. }
  1133. return false
  1134. }