kms.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright (C) 2019-2023 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
  15. import (
  16. "fmt"
  17. "os/exec"
  18. "path/filepath"
  19. "github.com/hashicorp/go-hclog"
  20. "github.com/hashicorp/go-plugin"
  21. sdkkms "github.com/sftpgo/sdk/kms"
  22. kmsplugin "github.com/sftpgo/sdk/plugin/kms"
  23. "github.com/drakkan/sftpgo/v2/internal/kms"
  24. "github.com/drakkan/sftpgo/v2/internal/logger"
  25. "github.com/drakkan/sftpgo/v2/internal/util"
  26. )
  27. var (
  28. validKMSSchemes = []string{sdkkms.SchemeAWS, sdkkms.SchemeGCP, sdkkms.SchemeVaultTransit, sdkkms.SchemeAzureKeyVault}
  29. validKMSEncryptedStatuses = []string{sdkkms.SecretStatusVaultTransit, sdkkms.SecretStatusAWS, sdkkms.SecretStatusGCP,
  30. sdkkms.SecretStatusAzureKeyVault}
  31. )
  32. // KMSConfig defines configuration parameters for kms plugins
  33. type KMSConfig struct {
  34. Scheme string `json:"scheme" mapstructure:"scheme"`
  35. EncryptedStatus string `json:"encrypted_status" mapstructure:"encrypted_status"`
  36. }
  37. func (c *KMSConfig) validate() error {
  38. if !util.Contains(validKMSSchemes, c.Scheme) {
  39. return fmt.Errorf("invalid kms scheme: %v", c.Scheme)
  40. }
  41. if !util.Contains(validKMSEncryptedStatuses, c.EncryptedStatus) {
  42. return fmt.Errorf("invalid kms encrypted status: %v", c.EncryptedStatus)
  43. }
  44. return nil
  45. }
  46. type kmsPlugin struct {
  47. config Config
  48. service kmsplugin.Service
  49. client *plugin.Client
  50. }
  51. func newKMSPlugin(config Config) (*kmsPlugin, error) {
  52. p := &kmsPlugin{
  53. config: config,
  54. }
  55. if err := p.initialize(); err != nil {
  56. logger.Warn(logSender, "", "unable to create kms plugin: %v, config %+v", err, config)
  57. return nil, err
  58. }
  59. return p, nil
  60. }
  61. func (p *kmsPlugin) initialize() error {
  62. killProcess(p.config.Cmd)
  63. logger.Debug(logSender, "", "create new kms plugin %q", p.config.Cmd)
  64. if err := p.config.KMSOptions.validate(); err != nil {
  65. return fmt.Errorf("invalid options for kms plugin %q: %v", p.config.Cmd, err)
  66. }
  67. secureConfig, err := p.config.getSecureConfig()
  68. if err != nil {
  69. return err
  70. }
  71. client := plugin.NewClient(&plugin.ClientConfig{
  72. HandshakeConfig: kmsplugin.Handshake,
  73. Plugins: kmsplugin.PluginMap,
  74. Cmd: exec.Command(p.config.Cmd, p.config.Args...),
  75. AllowedProtocols: []plugin.Protocol{
  76. plugin.ProtocolGRPC,
  77. },
  78. AutoMTLS: p.config.AutoMTLS,
  79. SecureConfig: secureConfig,
  80. Managed: false,
  81. Logger: &logger.HCLogAdapter{
  82. Logger: hclog.New(&hclog.LoggerOptions{
  83. Name: fmt.Sprintf("%v.%v", logSender, kmsplugin.PluginName),
  84. Level: pluginsLogLevel,
  85. DisableTime: true,
  86. }),
  87. },
  88. })
  89. rpcClient, err := client.Client()
  90. if err != nil {
  91. logger.Debug(logSender, "", "unable to get rpc client for kms plugin %q: %v", p.config.Cmd, err)
  92. return err
  93. }
  94. raw, err := rpcClient.Dispense(kmsplugin.PluginName)
  95. if err != nil {
  96. logger.Debug(logSender, "", "unable to get plugin %v from rpc client for command %q: %v",
  97. kmsplugin.PluginName, p.config.Cmd, err)
  98. return err
  99. }
  100. p.client = client
  101. p.service = raw.(kmsplugin.Service)
  102. return nil
  103. }
  104. func (p *kmsPlugin) exited() bool {
  105. return p.client.Exited()
  106. }
  107. func (p *kmsPlugin) cleanup() {
  108. p.client.Kill()
  109. }
  110. func (p *kmsPlugin) Encrypt(secret kms.BaseSecret, url string, masterKey string) (string, string, int32, error) {
  111. return p.service.Encrypt(secret.Payload, secret.AdditionalData, url, masterKey)
  112. }
  113. func (p *kmsPlugin) Decrypt(secret kms.BaseSecret, url string, masterKey string) (string, error) {
  114. return p.service.Decrypt(secret.Payload, secret.Key, secret.AdditionalData, secret.Mode, url, masterKey)
  115. }
  116. type kmsPluginSecretProvider struct {
  117. kms.BaseSecret
  118. URL string
  119. MasterKey string
  120. config *Config
  121. }
  122. func (s *kmsPluginSecretProvider) Name() string {
  123. return fmt.Sprintf("KMSPlugin_%v_%v_%v", filepath.Base(s.config.Cmd), s.config.KMSOptions.Scheme, s.config.kmsID)
  124. }
  125. func (s *kmsPluginSecretProvider) IsEncrypted() bool {
  126. return s.Status == s.config.KMSOptions.EncryptedStatus
  127. }
  128. func (s *kmsPluginSecretProvider) Encrypt() error {
  129. if s.Status != sdkkms.SecretStatusPlain {
  130. return kms.ErrWrongSecretStatus
  131. }
  132. if s.Payload == "" {
  133. return kms.ErrInvalidSecret
  134. }
  135. payload, key, mode, err := Handler.kmsEncrypt(s.BaseSecret, s.URL, s.MasterKey, s.config.kmsID)
  136. if err != nil {
  137. return err
  138. }
  139. s.Status = s.config.KMSOptions.EncryptedStatus
  140. s.Payload = payload
  141. s.Key = key
  142. s.Mode = int(mode)
  143. return nil
  144. }
  145. func (s *kmsPluginSecretProvider) Decrypt() error {
  146. if !s.IsEncrypted() {
  147. return kms.ErrWrongSecretStatus
  148. }
  149. payload, err := Handler.kmsDecrypt(s.BaseSecret, s.URL, s.MasterKey, s.config.kmsID)
  150. if err != nil {
  151. return err
  152. }
  153. s.Status = sdkkms.SecretStatusPlain
  154. s.Payload = payload
  155. s.Key = ""
  156. s.AdditionalData = ""
  157. s.Mode = 0
  158. return nil
  159. }
  160. func (s *kmsPluginSecretProvider) Clone() kms.SecretProvider {
  161. baseSecret := kms.BaseSecret{
  162. Status: s.Status,
  163. Payload: s.Payload,
  164. Key: s.Key,
  165. AdditionalData: s.AdditionalData,
  166. Mode: s.Mode,
  167. }
  168. return s.config.newKMSPluginSecretProvider(baseSecret, s.URL, s.MasterKey)
  169. }