kms.go 4.9 KB

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