plugin.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. // Copyright (C) 2019 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 plugin provides support for the SFTPGo plugin system
  15. package plugin
  16. import (
  17. "crypto/sha256"
  18. "crypto/x509"
  19. "encoding/hex"
  20. "errors"
  21. "fmt"
  22. "os"
  23. "os/exec"
  24. "path/filepath"
  25. "slices"
  26. "strings"
  27. "sync"
  28. "sync/atomic"
  29. "time"
  30. "github.com/hashicorp/go-hclog"
  31. "github.com/hashicorp/go-plugin"
  32. "github.com/sftpgo/sdk/plugin/auth"
  33. "github.com/sftpgo/sdk/plugin/eventsearcher"
  34. "github.com/sftpgo/sdk/plugin/ipfilter"
  35. kmsplugin "github.com/sftpgo/sdk/plugin/kms"
  36. "github.com/sftpgo/sdk/plugin/notifier"
  37. "github.com/drakkan/sftpgo/v2/internal/kms"
  38. "github.com/drakkan/sftpgo/v2/internal/logger"
  39. "github.com/drakkan/sftpgo/v2/internal/util"
  40. )
  41. const (
  42. logSender = "plugins"
  43. )
  44. var (
  45. // Handler defines the plugins manager
  46. Handler Manager
  47. pluginsLogLevel = hclog.Debug
  48. // ErrNoSearcher defines the error to return for events searches if no plugin is configured
  49. ErrNoSearcher = errors.New("no events searcher plugin defined")
  50. )
  51. // Renderer defines the interface for generic objects rendering
  52. type Renderer interface {
  53. RenderAsJSON(reload bool) ([]byte, error)
  54. }
  55. // Config defines a plugin configuration
  56. type Config struct {
  57. // Plugin type
  58. Type string `json:"type" mapstructure:"type"`
  59. // NotifierOptions defines options for notifiers plugins
  60. NotifierOptions NotifierConfig `json:"notifier_options" mapstructure:"notifier_options"`
  61. // KMSOptions defines options for a KMS plugin
  62. KMSOptions KMSConfig `json:"kms_options" mapstructure:"kms_options"`
  63. // AuthOptions defines options for authentication plugins
  64. AuthOptions AuthConfig `json:"auth_options" mapstructure:"auth_options"`
  65. // Path to the plugin executable
  66. Cmd string `json:"cmd" mapstructure:"cmd"`
  67. // Args to pass to the plugin executable
  68. Args []string `json:"args" mapstructure:"args"`
  69. // SHA256 checksum for the plugin executable.
  70. // If not empty it will be used to verify the integrity of the executable
  71. SHA256Sum string `json:"sha256sum" mapstructure:"sha256sum"`
  72. // If enabled the client and the server automatically negotiate mTLS for
  73. // transport authentication. This ensures that only the original client will
  74. // be allowed to connect to the server, and all other connections will be
  75. // rejected. The client will also refuse to connect to any server that isn't
  76. // the original instance started by the client.
  77. AutoMTLS bool `json:"auto_mtls" mapstructure:"auto_mtls"`
  78. // EnvPrefix defines the prefix for env vars to pass from the SFTPGo process
  79. // environment to the plugin. Set to "none" to not pass any environment
  80. // variable, set to "*" to pass all environment variables. If empty, the
  81. // prefix is returned as the plugin name in uppercase with "-" replaced with
  82. // "_" and a trailing "_". For example if the plugin name is
  83. // sftpgo-plugin-eventsearch the prefix will be SFTPGO_PLUGIN_EVENTSEARCH_
  84. EnvPrefix string `json:"env_prefix" mapstructure:"env_prefix"`
  85. // Additional environment variable names to pass from the SFTPGo process
  86. // environment to the plugin.
  87. EnvVars []string `json:"env_vars" mapstructure:"env_vars"`
  88. // unique identifier for kms plugins
  89. kmsID int
  90. }
  91. func (c *Config) getSecureConfig() (*plugin.SecureConfig, error) {
  92. if c.SHA256Sum != "" {
  93. checksum, err := hex.DecodeString(c.SHA256Sum)
  94. if err != nil {
  95. return nil, fmt.Errorf("invalid sha256 hash %q: %w", c.SHA256Sum, err)
  96. }
  97. return &plugin.SecureConfig{
  98. Checksum: checksum,
  99. Hash: sha256.New(),
  100. }, nil
  101. }
  102. return nil, nil
  103. }
  104. func (c *Config) getEnvVarPrefix() string {
  105. if c.EnvPrefix == "none" {
  106. return ""
  107. }
  108. if c.EnvPrefix != "" {
  109. return c.EnvPrefix
  110. }
  111. baseName := filepath.Base(c.Cmd)
  112. name := strings.TrimSuffix(baseName, filepath.Ext(baseName))
  113. prefix := strings.ToUpper(name) + "_"
  114. return strings.ReplaceAll(prefix, "-", "_")
  115. }
  116. func (c *Config) getCommand() *exec.Cmd {
  117. cmd := exec.Command(c.Cmd, c.Args...)
  118. cmd.Env = []string{}
  119. if envVarPrefix := c.getEnvVarPrefix(); envVarPrefix != "" {
  120. if envVarPrefix == "*" {
  121. logger.Debug(logSender, "", "sharing all the environment variables with plugin %q", c.Cmd)
  122. cmd.Env = append(cmd.Env, os.Environ()...)
  123. return cmd
  124. }
  125. logger.Debug(logSender, "", "adding env vars with prefix %q for plugin %q", envVarPrefix, c.Cmd)
  126. for _, val := range os.Environ() {
  127. if strings.HasPrefix(val, envVarPrefix) {
  128. cmd.Env = append(cmd.Env, val)
  129. }
  130. }
  131. }
  132. logger.Debug(logSender, "", "additional env vars for plugin %q: %+v", c.Cmd, c.EnvVars)
  133. for _, key := range c.EnvVars {
  134. cmd.Env = append(cmd.Env, os.Getenv(key))
  135. }
  136. return cmd
  137. }
  138. func (c *Config) newKMSPluginSecretProvider(base kms.BaseSecret, url, masterKey string) kms.SecretProvider {
  139. return &kmsPluginSecretProvider{
  140. BaseSecret: base,
  141. URL: url,
  142. MasterKey: masterKey,
  143. config: c,
  144. }
  145. }
  146. // Manager handles enabled plugins
  147. type Manager struct {
  148. closed atomic.Bool
  149. done chan bool
  150. // List of configured plugins
  151. Configs []Config `json:"plugins" mapstructure:"plugins"`
  152. notifLock sync.RWMutex
  153. notifiers []*notifierPlugin
  154. kmsLock sync.RWMutex
  155. kms []*kmsPlugin
  156. authLock sync.RWMutex
  157. auths []*authPlugin
  158. searcherLock sync.RWMutex
  159. searcher *searcherPlugin
  160. ipFilterLock sync.RWMutex
  161. filter *ipFilterPlugin
  162. authScopes int
  163. hasSearcher bool
  164. hasNotifiers bool
  165. hasAuths bool
  166. hasIPFilter bool
  167. concurrencyGuard chan struct{}
  168. }
  169. // Initialize initializes the configured plugins
  170. func Initialize(configs []Config, logLevel string) error {
  171. logger.Debug(logSender, "", "initialize")
  172. Handler = Manager{
  173. Configs: configs,
  174. done: make(chan bool),
  175. authScopes: -1,
  176. concurrencyGuard: make(chan struct{}, 250),
  177. }
  178. Handler.closed.Store(false)
  179. setLogLevel(logLevel)
  180. if len(configs) == 0 {
  181. return nil
  182. }
  183. if err := Handler.validateConfigs(); err != nil {
  184. return err
  185. }
  186. if err := initializePlugins(); err != nil {
  187. return err
  188. }
  189. startCheckTicker()
  190. return nil
  191. }
  192. func initializePlugins() error {
  193. kmsID := 0
  194. for idx, config := range Handler.Configs {
  195. switch config.Type {
  196. case notifier.PluginName:
  197. plugin, err := newNotifierPlugin(config)
  198. if err != nil {
  199. return err
  200. }
  201. Handler.notifiers = append(Handler.notifiers, plugin)
  202. case kmsplugin.PluginName:
  203. plugin, err := newKMSPlugin(config)
  204. if err != nil {
  205. return err
  206. }
  207. Handler.kms = append(Handler.kms, plugin)
  208. Handler.Configs[idx].kmsID = kmsID
  209. kmsID++
  210. kms.RegisterSecretProvider(config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus,
  211. Handler.Configs[idx].newKMSPluginSecretProvider)
  212. logger.Info(logSender, "", "registered secret provider for scheme: %v, encrypted status: %v",
  213. config.KMSOptions.Scheme, config.KMSOptions.EncryptedStatus)
  214. case auth.PluginName:
  215. plugin, err := newAuthPlugin(config)
  216. if err != nil {
  217. return err
  218. }
  219. Handler.auths = append(Handler.auths, plugin)
  220. if Handler.authScopes == -1 {
  221. Handler.authScopes = config.AuthOptions.Scope
  222. } else {
  223. Handler.authScopes |= config.AuthOptions.Scope
  224. }
  225. case eventsearcher.PluginName:
  226. plugin, err := newSearcherPlugin(config)
  227. if err != nil {
  228. return err
  229. }
  230. Handler.searcher = plugin
  231. case ipfilter.PluginName:
  232. plugin, err := newIPFilterPlugin(config)
  233. if err != nil {
  234. return err
  235. }
  236. Handler.filter = plugin
  237. default:
  238. return fmt.Errorf("unsupported plugin type: %v", config.Type)
  239. }
  240. }
  241. return nil
  242. }
  243. func (m *Manager) validateConfigs() error {
  244. kmsSchemes := make(map[string]bool)
  245. kmsEncryptions := make(map[string]bool)
  246. m.hasSearcher = false
  247. m.hasNotifiers = false
  248. m.hasAuths = false
  249. m.hasIPFilter = false
  250. for _, config := range m.Configs {
  251. switch config.Type {
  252. case kmsplugin.PluginName:
  253. if _, ok := kmsSchemes[config.KMSOptions.Scheme]; ok {
  254. return fmt.Errorf("invalid KMS configuration, duplicated scheme %q", config.KMSOptions.Scheme)
  255. }
  256. if _, ok := kmsEncryptions[config.KMSOptions.EncryptedStatus]; ok {
  257. return fmt.Errorf("invalid KMS configuration, duplicated encrypted status %q", config.KMSOptions.EncryptedStatus)
  258. }
  259. kmsSchemes[config.KMSOptions.Scheme] = true
  260. kmsEncryptions[config.KMSOptions.EncryptedStatus] = true
  261. case eventsearcher.PluginName:
  262. if m.hasSearcher {
  263. return errors.New("only one eventsearcher plugin can be defined")
  264. }
  265. m.hasSearcher = true
  266. case notifier.PluginName:
  267. m.hasNotifiers = true
  268. case auth.PluginName:
  269. m.hasAuths = true
  270. case ipfilter.PluginName:
  271. m.hasIPFilter = true
  272. }
  273. }
  274. return nil
  275. }
  276. // HasAuthenticators returns true if there is at least an auth plugin
  277. func (m *Manager) HasAuthenticators() bool {
  278. return m.hasAuths
  279. }
  280. // HasNotifiers returns true if there is at least a notifier plugin
  281. func (m *Manager) HasNotifiers() bool {
  282. return m.hasNotifiers
  283. }
  284. // NotifyFsEvent sends the fs event notifications using any defined notifier plugins
  285. func (m *Manager) NotifyFsEvent(event *notifier.FsEvent) {
  286. m.notifLock.RLock()
  287. defer m.notifLock.RUnlock()
  288. for _, n := range m.notifiers {
  289. n.notifyFsAction(event)
  290. }
  291. }
  292. // NotifyProviderEvent sends the provider event notifications using any defined notifier plugins
  293. func (m *Manager) NotifyProviderEvent(event *notifier.ProviderEvent, object Renderer) {
  294. m.notifLock.RLock()
  295. defer m.notifLock.RUnlock()
  296. for _, n := range m.notifiers {
  297. n.notifyProviderAction(event, object)
  298. }
  299. }
  300. // NotifyLogEvent sends the log event notifications using any defined notifier plugins
  301. func (m *Manager) NotifyLogEvent(event notifier.LogEventType, protocol, username, ip, role string, err error) {
  302. if !m.hasNotifiers {
  303. return
  304. }
  305. m.notifLock.RLock()
  306. defer m.notifLock.RUnlock()
  307. var e *notifier.LogEvent
  308. for _, n := range m.notifiers {
  309. if slices.Contains(n.config.NotifierOptions.LogEvents, int(event)) {
  310. if e == nil {
  311. message := ""
  312. if err != nil {
  313. message = err.Error()
  314. }
  315. e = &notifier.LogEvent{
  316. Timestamp: time.Now().UnixNano(),
  317. Event: event,
  318. Protocol: protocol,
  319. Username: username,
  320. IP: ip,
  321. Message: message,
  322. Role: role,
  323. }
  324. }
  325. n.notifyLogEvent(e)
  326. }
  327. }
  328. }
  329. // HasSearcher returns true if an event searcher plugin is defined
  330. func (m *Manager) HasSearcher() bool {
  331. return m.hasSearcher
  332. }
  333. // SearchFsEvents returns the filesystem events matching the specified filters
  334. func (m *Manager) SearchFsEvents(searchFilters *eventsearcher.FsEventSearch) ([]byte, error) {
  335. if !m.hasSearcher {
  336. return nil, ErrNoSearcher
  337. }
  338. m.searcherLock.RLock()
  339. plugin := m.searcher
  340. m.searcherLock.RUnlock()
  341. return plugin.searchear.SearchFsEvents(searchFilters)
  342. }
  343. // SearchProviderEvents returns the provider events matching the specified filters
  344. func (m *Manager) SearchProviderEvents(searchFilters *eventsearcher.ProviderEventSearch) ([]byte, error) {
  345. if !m.hasSearcher {
  346. return nil, ErrNoSearcher
  347. }
  348. m.searcherLock.RLock()
  349. plugin := m.searcher
  350. m.searcherLock.RUnlock()
  351. return plugin.searchear.SearchProviderEvents(searchFilters)
  352. }
  353. // SearchLogEvents returns the log events matching the specified filters
  354. func (m *Manager) SearchLogEvents(searchFilters *eventsearcher.LogEventSearch) ([]byte, error) {
  355. if !m.hasSearcher {
  356. return nil, ErrNoSearcher
  357. }
  358. m.searcherLock.RLock()
  359. plugin := m.searcher
  360. m.searcherLock.RUnlock()
  361. return plugin.searchear.SearchLogEvents(searchFilters)
  362. }
  363. // IsIPBanned returns true if the IP filter plugin does not allow the specified ip.
  364. // If no IP filter plugin is defined this method returns false
  365. func (m *Manager) IsIPBanned(ip, protocol string) bool {
  366. if !m.hasIPFilter {
  367. return false
  368. }
  369. m.ipFilterLock.RLock()
  370. plugin := m.filter
  371. m.ipFilterLock.RUnlock()
  372. if plugin.exited() {
  373. logger.Warn(logSender, "", "ip filter plugin is not active, cannot check ip %q", ip)
  374. return false
  375. }
  376. return plugin.filter.CheckIP(ip, protocol) != nil
  377. }
  378. // ReloadFilter sends a reload request to the IP filter plugin
  379. func (m *Manager) ReloadFilter() {
  380. if !m.hasIPFilter {
  381. return
  382. }
  383. m.ipFilterLock.RLock()
  384. plugin := m.filter
  385. m.ipFilterLock.RUnlock()
  386. if err := plugin.filter.Reload(); err != nil {
  387. logger.Error(logSender, "", "unable to reload IP filter plugin: %v", err)
  388. }
  389. }
  390. func (m *Manager) kmsEncrypt(secret kms.BaseSecret, url string, masterKey string, kmsID int) (string, string, int32, error) {
  391. m.kmsLock.RLock()
  392. plugin := m.kms[kmsID]
  393. m.kmsLock.RUnlock()
  394. return plugin.Encrypt(secret, url, masterKey)
  395. }
  396. func (m *Manager) kmsDecrypt(secret kms.BaseSecret, url string, masterKey string, kmsID int) (string, error) {
  397. m.kmsLock.RLock()
  398. plugin := m.kms[kmsID]
  399. m.kmsLock.RUnlock()
  400. return plugin.Decrypt(secret, url, masterKey)
  401. }
  402. // HasAuthScope returns true if there is an auth plugin that support the specified scope
  403. func (m *Manager) HasAuthScope(scope int) bool {
  404. if m.authScopes == -1 {
  405. return false
  406. }
  407. return m.authScopes&scope != 0
  408. }
  409. // Authenticate tries to authenticate the specified user using an external plugin
  410. func (m *Manager) Authenticate(username, password, ip, protocol string, pkey string,
  411. tlsCert *x509.Certificate, authScope int, userAsJSON []byte,
  412. ) ([]byte, error) {
  413. switch authScope {
  414. case AuthScopePassword:
  415. return m.checkUserAndPass(username, password, ip, protocol, userAsJSON)
  416. case AuthScopePublicKey:
  417. return m.checkUserAndPublicKey(username, pkey, ip, protocol, userAsJSON)
  418. case AuthScopeKeyboardInteractive:
  419. return m.checkUserAndKeyboardInteractive(username, ip, protocol, userAsJSON)
  420. case AuthScopeTLSCertificate:
  421. cert, err := util.EncodeTLSCertToPem(tlsCert)
  422. if err != nil {
  423. logger.Error(logSender, "", "unable to encode tls certificate to pem: %v", err)
  424. return nil, fmt.Errorf("unable to encode tls cert to pem: %w", err)
  425. }
  426. return m.checkUserAndTLSCert(username, cert, ip, protocol, userAsJSON)
  427. default:
  428. return nil, fmt.Errorf("unsupported auth scope: %v", authScope)
  429. }
  430. }
  431. // ExecuteKeyboardInteractiveStep executes a keyboard interactive step
  432. func (m *Manager) ExecuteKeyboardInteractiveStep(req *KeyboardAuthRequest) (*KeyboardAuthResponse, error) {
  433. var plugin *authPlugin
  434. m.authLock.Lock()
  435. for _, p := range m.auths {
  436. if p.config.AuthOptions.Scope&AuthScopePassword != 0 {
  437. plugin = p
  438. break
  439. }
  440. }
  441. m.authLock.Unlock()
  442. if plugin == nil {
  443. return nil, errors.New("no auth plugin configured for keyaboard interactive authentication step")
  444. }
  445. return plugin.sendKeyboardIteractiveRequest(req)
  446. }
  447. func (m *Manager) checkUserAndPass(username, password, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  448. var plugin *authPlugin
  449. m.authLock.Lock()
  450. for _, p := range m.auths {
  451. if p.config.AuthOptions.Scope&AuthScopePassword != 0 {
  452. plugin = p
  453. break
  454. }
  455. }
  456. m.authLock.Unlock()
  457. if plugin == nil {
  458. return nil, errors.New("no auth plugin configured for password checking")
  459. }
  460. return plugin.checkUserAndPass(username, password, ip, protocol, userAsJSON)
  461. }
  462. func (m *Manager) checkUserAndPublicKey(username, pubKey, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  463. var plugin *authPlugin
  464. m.authLock.Lock()
  465. for _, p := range m.auths {
  466. if p.config.AuthOptions.Scope&AuthScopePublicKey != 0 {
  467. plugin = p
  468. break
  469. }
  470. }
  471. m.authLock.Unlock()
  472. if plugin == nil {
  473. return nil, errors.New("no auth plugin configured for public key checking")
  474. }
  475. return plugin.checkUserAndPublicKey(username, pubKey, ip, protocol, userAsJSON)
  476. }
  477. func (m *Manager) checkUserAndTLSCert(username, tlsCert, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  478. var plugin *authPlugin
  479. m.authLock.Lock()
  480. for _, p := range m.auths {
  481. if p.config.AuthOptions.Scope&AuthScopeTLSCertificate != 0 {
  482. plugin = p
  483. break
  484. }
  485. }
  486. m.authLock.Unlock()
  487. if plugin == nil {
  488. return nil, errors.New("no auth plugin configured for TLS certificate checking")
  489. }
  490. return plugin.checkUserAndTLSCertificate(username, tlsCert, ip, protocol, userAsJSON)
  491. }
  492. func (m *Manager) checkUserAndKeyboardInteractive(username, ip, protocol string, userAsJSON []byte) ([]byte, error) {
  493. var plugin *authPlugin
  494. m.authLock.Lock()
  495. for _, p := range m.auths {
  496. if p.config.AuthOptions.Scope&AuthScopeKeyboardInteractive != 0 {
  497. plugin = p
  498. break
  499. }
  500. }
  501. m.authLock.Unlock()
  502. if plugin == nil {
  503. return nil, errors.New("no auth plugin configured for keyboard interactive checking")
  504. }
  505. return plugin.checkUserAndKeyboardInteractive(username, ip, protocol, userAsJSON)
  506. }
  507. func (m *Manager) checkCrashedPlugins() {
  508. m.notifLock.RLock()
  509. for idx, n := range m.notifiers {
  510. if n.exited() {
  511. defer func(cfg Config, index int) {
  512. Handler.restartNotifierPlugin(cfg, index)
  513. }(n.config, idx)
  514. } else {
  515. n.sendQueuedEvents()
  516. }
  517. }
  518. m.notifLock.RUnlock()
  519. m.kmsLock.RLock()
  520. for idx, k := range m.kms {
  521. if k.exited() {
  522. defer func(cfg Config, index int) {
  523. Handler.restartKMSPlugin(cfg, index)
  524. }(k.config, idx)
  525. }
  526. }
  527. m.kmsLock.RUnlock()
  528. m.authLock.RLock()
  529. for idx, a := range m.auths {
  530. if a.exited() {
  531. defer func(cfg Config, index int) {
  532. Handler.restartAuthPlugin(cfg, index)
  533. }(a.config, idx)
  534. }
  535. }
  536. m.authLock.RUnlock()
  537. if m.hasSearcher {
  538. m.searcherLock.RLock()
  539. if m.searcher.exited() {
  540. defer func(cfg Config) {
  541. Handler.restartSearcherPlugin(cfg)
  542. }(m.searcher.config)
  543. }
  544. m.searcherLock.RUnlock()
  545. }
  546. if m.hasIPFilter {
  547. m.ipFilterLock.RLock()
  548. if m.filter.exited() {
  549. defer func(cfg Config) {
  550. Handler.restartIPFilterPlugin(cfg)
  551. }(m.filter.config)
  552. }
  553. m.ipFilterLock.RUnlock()
  554. }
  555. }
  556. func (m *Manager) restartNotifierPlugin(config Config, idx int) {
  557. if m.closed.Load() {
  558. return
  559. }
  560. logger.Info(logSender, "", "try to restart crashed notifier plugin %q, idx: %v", config.Cmd, idx)
  561. plugin, err := newNotifierPlugin(config)
  562. if err != nil {
  563. logger.Error(logSender, "", "unable to restart notifier plugin %q, err: %v", config.Cmd, err)
  564. return
  565. }
  566. m.notifLock.Lock()
  567. plugin.queue = m.notifiers[idx].queue
  568. m.notifiers[idx] = plugin
  569. m.notifLock.Unlock()
  570. plugin.sendQueuedEvents()
  571. }
  572. func (m *Manager) restartKMSPlugin(config Config, idx int) {
  573. if m.closed.Load() {
  574. return
  575. }
  576. logger.Info(logSender, "", "try to restart crashed kms plugin %q, idx: %v", config.Cmd, idx)
  577. plugin, err := newKMSPlugin(config)
  578. if err != nil {
  579. logger.Error(logSender, "", "unable to restart kms plugin %q, err: %v", config.Cmd, err)
  580. return
  581. }
  582. m.kmsLock.Lock()
  583. m.kms[idx] = plugin
  584. m.kmsLock.Unlock()
  585. }
  586. func (m *Manager) restartAuthPlugin(config Config, idx int) {
  587. if m.closed.Load() {
  588. return
  589. }
  590. logger.Info(logSender, "", "try to restart crashed auth plugin %q, idx: %v", config.Cmd, idx)
  591. plugin, err := newAuthPlugin(config)
  592. if err != nil {
  593. logger.Error(logSender, "", "unable to restart auth plugin %q, err: %v", config.Cmd, err)
  594. return
  595. }
  596. m.authLock.Lock()
  597. m.auths[idx] = plugin
  598. m.authLock.Unlock()
  599. }
  600. func (m *Manager) restartSearcherPlugin(config Config) {
  601. if m.closed.Load() {
  602. return
  603. }
  604. logger.Info(logSender, "", "try to restart crashed searcher plugin %q", config.Cmd)
  605. plugin, err := newSearcherPlugin(config)
  606. if err != nil {
  607. logger.Error(logSender, "", "unable to restart searcher plugin %q, err: %v", config.Cmd, err)
  608. return
  609. }
  610. m.searcherLock.Lock()
  611. m.searcher = plugin
  612. m.searcherLock.Unlock()
  613. }
  614. func (m *Manager) restartIPFilterPlugin(config Config) {
  615. if m.closed.Load() {
  616. return
  617. }
  618. logger.Info(logSender, "", "try to restart crashed IP filter plugin %q", config.Cmd)
  619. plugin, err := newIPFilterPlugin(config)
  620. if err != nil {
  621. logger.Error(logSender, "", "unable to restart IP filter plugin %q, err: %v", config.Cmd, err)
  622. return
  623. }
  624. m.ipFilterLock.Lock()
  625. m.filter = plugin
  626. m.ipFilterLock.Unlock()
  627. }
  628. func (m *Manager) addTask() {
  629. m.concurrencyGuard <- struct{}{}
  630. }
  631. func (m *Manager) removeTask() {
  632. <-m.concurrencyGuard
  633. }
  634. // Cleanup releases all the active plugins
  635. func (m *Manager) Cleanup() {
  636. if m.closed.Swap(true) {
  637. return
  638. }
  639. logger.Debug(logSender, "", "cleanup")
  640. close(m.done)
  641. m.notifLock.Lock()
  642. for _, n := range m.notifiers {
  643. logger.Debug(logSender, "", "cleanup notifier plugin %v", n.config.Cmd)
  644. n.cleanup()
  645. }
  646. m.notifLock.Unlock()
  647. m.kmsLock.Lock()
  648. for _, k := range m.kms {
  649. logger.Debug(logSender, "", "cleanup kms plugin %v", k.config.Cmd)
  650. k.cleanup()
  651. }
  652. m.kmsLock.Unlock()
  653. m.authLock.Lock()
  654. for _, a := range m.auths {
  655. logger.Debug(logSender, "", "cleanup auth plugin %v", a.config.Cmd)
  656. a.cleanup()
  657. }
  658. m.authLock.Unlock()
  659. if m.hasSearcher {
  660. m.searcherLock.Lock()
  661. logger.Debug(logSender, "", "cleanup searcher plugin %v", m.searcher.config.Cmd)
  662. m.searcher.cleanup()
  663. m.searcherLock.Unlock()
  664. }
  665. if m.hasIPFilter {
  666. m.ipFilterLock.Lock()
  667. logger.Debug(logSender, "", "cleanup IP filter plugin %v", m.filter.config.Cmd)
  668. m.filter.cleanup()
  669. m.ipFilterLock.Unlock()
  670. }
  671. }
  672. func setLogLevel(logLevel string) {
  673. switch logLevel {
  674. case "info":
  675. pluginsLogLevel = hclog.Info
  676. case "warn":
  677. pluginsLogLevel = hclog.Warn
  678. case "error":
  679. pluginsLogLevel = hclog.Error
  680. default:
  681. pluginsLogLevel = hclog.Debug
  682. }
  683. }
  684. func startCheckTicker() {
  685. logger.Debug(logSender, "", "start plugins checker")
  686. go func() {
  687. ticker := time.NewTicker(30 * time.Second)
  688. defer ticker.Stop()
  689. for {
  690. select {
  691. case <-Handler.done:
  692. logger.Debug(logSender, "", "handler done, stop plugins checker")
  693. return
  694. case <-ticker.C:
  695. Handler.checkCrashedPlugins()
  696. }
  697. }
  698. }()
  699. }