kms.go 5.6 KB

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