client.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package keepersecurity
  13. import (
  14. "context"
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "regexp"
  19. "strings"
  20. ksm "github.com/keeper-security/secrets-manager-go/core"
  21. "golang.org/x/exp/maps"
  22. corev1 "k8s.io/api/core/v1"
  23. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  24. )
  25. const (
  26. errKeeperSecuritySecretsNotFound = "unable to find secrets. %w"
  27. errKeeperSecuritySecretNotFound = "unable to find secret %s. Error: %w"
  28. errKeeperSecuritySecretNotUnique = "more than 1 secret %s found"
  29. errKeeperSecurityNoSecretsFound = "no secrets found"
  30. errKeeperSecurityInvalidSecretInvalidFormat = "invalid secret. Invalid format: %w"
  31. errKeeperSecurityInvalidSecretDuplicatedKey = "invalid Secret. Following keys are duplicated %s"
  32. errKeeperSecurityInvalidProperty = "invalid Property. Secret %s does not have any key matching %s"
  33. errKeeperSecurityInvalidField = "invalid Field. Key %s does not exists"
  34. errKeeperSecurityNoFields = "invalid Secret. Secret %s does not contain any valid field/file"
  35. keeperSecurityFileRef = "fileRef"
  36. keeperSecurityMfa = "oneTimeCode"
  37. errTagsNotImplemented = "'find.tags' is not implemented in the KeeperSecurity provider"
  38. errPathNotImplemented = "'find.path' is not implemented in the KeeperSecurity provider"
  39. errInvalidJSONSecret = "invalid Secret. Secret %s can not be converted to JSON. %w"
  40. errInvalidRegex = "find.name.regex. Invalid Regular expresion %s. %w"
  41. errInvalidRemoteRefKey = "match.remoteRef.remoteKey. Invalid format. Format should match secretName/key got %s"
  42. errInvalidSecretType = "ESO can only push/delete %s record types. Secret %s is type %s"
  43. errFieldNotFound = "secret %s does not contain any custom field with label %s"
  44. externalSecretType = "externalSecrets"
  45. secretType = "secret"
  46. LoginType = "login"
  47. LoginTypeExpr = "login|username"
  48. PasswordType = "password"
  49. URLTypeExpr = "url|baseurl"
  50. URLType = "url"
  51. )
  52. type Client struct {
  53. ksmClient SecurityClient
  54. folderID string
  55. }
  56. type SecurityClient interface {
  57. GetSecrets(filter []string) ([]*ksm.Record, error)
  58. GetSecretByTitle(recordTitle string) (*ksm.Record, error)
  59. CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
  60. DeleteSecrets(recrecordUids []string) (map[string]string, error)
  61. Save(record *ksm.Record) error
  62. }
  63. type Field struct {
  64. Type string `json:"type"`
  65. Value []interface{} `json:"value"`
  66. }
  67. type CustomField struct {
  68. Type string `json:"type"`
  69. Label string `json:"label"`
  70. Value []interface{} `json:"value"`
  71. }
  72. type File struct {
  73. Title string `json:"type"`
  74. Content string `json:"content"`
  75. }
  76. type Secret struct {
  77. Title string `json:"title"`
  78. Type string `json:"type"`
  79. Fields []Field `json:"fields"`
  80. Custom []CustomField `json:"custom"`
  81. Files []File `json:"files"`
  82. }
  83. func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
  84. return esv1beta1.ValidationResultReady, nil
  85. }
  86. func (c *Client) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  87. record, err := c.findSecretByID(ref.Key)
  88. if err != nil {
  89. return nil, err
  90. }
  91. secret, err := c.getValidKeeperSecret(record)
  92. if err != nil {
  93. return nil, err
  94. }
  95. return secret.getItem(ref)
  96. }
  97. func (c *Client) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  98. record, err := c.findSecretByID(ref.Key)
  99. if err != nil {
  100. return nil, err
  101. }
  102. secret, err := c.getValidKeeperSecret(record)
  103. if err != nil {
  104. return nil, err
  105. }
  106. return secret.getItems(ref)
  107. }
  108. func (c *Client) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  109. if ref.Tags != nil {
  110. return nil, fmt.Errorf(errTagsNotImplemented)
  111. }
  112. if ref.Path != nil {
  113. return nil, fmt.Errorf(errPathNotImplemented)
  114. }
  115. secretData := make(map[string][]byte)
  116. records, err := c.findSecrets()
  117. if err != nil {
  118. return nil, err
  119. }
  120. for _, record := range records {
  121. secret, err := c.getValidKeeperSecret(record)
  122. if err != nil {
  123. return nil, err
  124. }
  125. match, err := regexp.MatchString(ref.Name.RegExp, secret.Title)
  126. if err != nil {
  127. return nil, fmt.Errorf(errInvalidRegex, ref.Name.RegExp, err)
  128. }
  129. if !match {
  130. continue
  131. }
  132. secretData[secret.Title], err = secret.getItem(esv1beta1.ExternalSecretDataRemoteRef{})
  133. if err != nil {
  134. return nil, err
  135. }
  136. }
  137. return secretData, nil
  138. }
  139. func (c *Client) Close(_ context.Context) error {
  140. return nil
  141. }
  142. func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
  143. if data.GetSecretKey() == "" {
  144. return fmt.Errorf("pushing the whole secret is not yet implemented")
  145. }
  146. value := secret.Data[data.GetSecretKey()]
  147. parts, err := c.buildSecretNameAndKey(data)
  148. if err != nil {
  149. return err
  150. }
  151. record, err := c.findSecretByName(parts[0])
  152. if err != nil {
  153. _, err = c.createSecret(parts[0], parts[1], value)
  154. if err != nil {
  155. return err
  156. }
  157. }
  158. if record != nil {
  159. if record.Type() != externalSecretType {
  160. return fmt.Errorf(errInvalidSecretType, externalSecretType, record.Title(), record.Type())
  161. }
  162. err = c.updateSecret(record, parts[1], value)
  163. if err != nil {
  164. return err
  165. }
  166. }
  167. return nil
  168. }
  169. func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
  170. parts, err := c.buildSecretNameAndKey(remoteRef)
  171. if err != nil {
  172. return err
  173. }
  174. secret, err := c.findSecretByName(parts[0])
  175. if err != nil {
  176. return err
  177. }
  178. if secret.Type() != externalSecretType {
  179. return fmt.Errorf(errInvalidSecretType, externalSecretType, secret.Title(), secret.Type())
  180. }
  181. _, err = c.ksmClient.DeleteSecrets([]string{secret.Uid})
  182. if err != nil {
  183. return nil
  184. }
  185. return nil
  186. }
  187. func (c *Client) buildSecretNameAndKey(remoteRef esv1beta1.PushSecretRemoteRef) ([]string, error) {
  188. parts := strings.Split(remoteRef.GetRemoteKey(), "/")
  189. if len(parts) != 2 {
  190. return nil, fmt.Errorf(errInvalidRemoteRefKey, remoteRef.GetRemoteKey())
  191. }
  192. return parts, nil
  193. }
  194. func (c *Client) createSecret(name, key string, value []byte) (string, error) {
  195. normalizedKey := strings.ToLower(key)
  196. externalSecretRecord := ksm.NewRecordCreate(externalSecretType, name)
  197. login := regexp.MustCompile(LoginTypeExpr)
  198. pass := regexp.MustCompile(PasswordType)
  199. url := regexp.MustCompile(URLTypeExpr)
  200. switch {
  201. case login.MatchString(normalizedKey):
  202. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  203. ksm.NewLogin(string(value)),
  204. )
  205. case pass.MatchString(normalizedKey):
  206. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  207. ksm.NewPassword(string(value)),
  208. )
  209. case url.MatchString(normalizedKey):
  210. externalSecretRecord.Fields = append(externalSecretRecord.Fields,
  211. ksm.NewUrl(string(value)),
  212. )
  213. default:
  214. field := ksm.KeeperRecordField{Type: secretType, Label: key}
  215. externalSecretRecord.Custom = append(externalSecretRecord.Custom,
  216. ksm.Secret{KeeperRecordField: field, Value: []string{string(value)}},
  217. )
  218. }
  219. return c.ksmClient.CreateSecretWithRecordData("", c.folderID, externalSecretRecord)
  220. }
  221. func (c *Client) updateSecret(secret *ksm.Record, key string, value []byte) error {
  222. normalizedKey := strings.ToLower(key)
  223. login := regexp.MustCompile(LoginTypeExpr)
  224. pass := regexp.MustCompile(PasswordType)
  225. url := regexp.MustCompile(URLTypeExpr)
  226. custom := false
  227. switch {
  228. case login.MatchString(normalizedKey):
  229. secret.SetFieldValueSingle(LoginType, string(value))
  230. case pass.MatchString(normalizedKey):
  231. secret.SetPassword(string(value))
  232. case url.MatchString(normalizedKey):
  233. secret.SetFieldValueSingle(URLType, string(value))
  234. default:
  235. custom = true
  236. }
  237. if custom {
  238. field := secret.GetCustomFieldValueByLabel(key)
  239. if field != "" {
  240. secret.SetCustomFieldValueSingle(key, string(value))
  241. } else {
  242. return fmt.Errorf(errFieldNotFound, secret.Title(), key)
  243. }
  244. }
  245. return c.ksmClient.Save(secret)
  246. }
  247. func (c *Client) getValidKeeperSecret(secret *ksm.Record) (*Secret, error) {
  248. keeperSecret := Secret{}
  249. err := json.Unmarshal([]byte(secret.RawJson), &keeperSecret)
  250. if err != nil {
  251. return nil, fmt.Errorf(errKeeperSecurityInvalidSecretInvalidFormat, err)
  252. }
  253. keeperSecret.addFiles(secret.Files)
  254. err = keeperSecret.validate()
  255. if err != nil {
  256. return nil, err
  257. }
  258. return &keeperSecret, nil
  259. }
  260. func (c *Client) findSecrets() ([]*ksm.Record, error) {
  261. records, err := c.ksmClient.GetSecrets([]string{})
  262. if err != nil {
  263. return nil, fmt.Errorf(errKeeperSecuritySecretsNotFound, err)
  264. }
  265. return records, nil
  266. }
  267. func (c *Client) findSecretByID(id string) (*ksm.Record, error) {
  268. records, err := c.ksmClient.GetSecrets([]string{id})
  269. if err != nil {
  270. return nil, fmt.Errorf(errKeeperSecuritySecretNotFound, id, err)
  271. }
  272. if len(records) == 0 {
  273. return nil, errors.New(errKeeperSecurityNoSecretsFound)
  274. }
  275. if len(records) > 1 {
  276. return nil, fmt.Errorf(errKeeperSecuritySecretNotUnique, id)
  277. }
  278. return records[0], nil
  279. }
  280. func (c *Client) findSecretByName(name string) (*ksm.Record, error) {
  281. record, err := c.ksmClient.GetSecretByTitle(name)
  282. if err != nil {
  283. return nil, err
  284. }
  285. return record, nil
  286. }
  287. func (s *Secret) validate() error {
  288. fields := make(map[string]int)
  289. for _, field := range s.Fields {
  290. fields[field.Type]++
  291. }
  292. for _, customField := range s.Custom {
  293. fields[customField.Label]++
  294. }
  295. for _, file := range s.Files {
  296. fields[file.Title]++
  297. }
  298. var duplicates []string
  299. for key, ocurrences := range fields {
  300. if ocurrences > 1 {
  301. duplicates = append(duplicates, key)
  302. }
  303. }
  304. if len(duplicates) != 0 {
  305. return fmt.Errorf(errKeeperSecurityInvalidSecretDuplicatedKey, strings.Join(duplicates, ", "))
  306. }
  307. return nil
  308. }
  309. func (s *Secret) addFiles(keeperFiles []*ksm.KeeperFile) {
  310. for _, f := range keeperFiles {
  311. s.Files = append(
  312. s.Files,
  313. File{
  314. Title: f.Title,
  315. Content: string(f.GetFileData()),
  316. },
  317. )
  318. }
  319. }
  320. func (s *Secret) getItem(ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  321. if ref.Property != "" {
  322. return s.getProperty(ref.Property)
  323. }
  324. secret, err := s.toString()
  325. return []byte(secret), err
  326. }
  327. func (s *Secret) getItems(ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  328. secretData := make(map[string][]byte)
  329. if ref.Property != "" {
  330. value, err := s.getProperty(ref.Property)
  331. if err != nil {
  332. return nil, err
  333. }
  334. secretData[ref.Property] = value
  335. return secretData, nil
  336. }
  337. fields := s.getFields()
  338. maps.Copy(secretData, fields)
  339. customFields := s.getCustomFields()
  340. maps.Copy(secretData, customFields)
  341. files := s.getFiles()
  342. maps.Copy(secretData, files)
  343. if len(secretData) == 0 {
  344. return nil, fmt.Errorf(errKeeperSecurityNoFields, s.Title)
  345. }
  346. return secretData, nil
  347. }
  348. func getFieldValue(value []interface{}) []byte {
  349. if len(value) < 1 {
  350. return []byte{}
  351. } else if len(value) == 1 {
  352. res, _ := json.Marshal(value[0])
  353. if str, ok := value[0].(string); ok {
  354. res = []byte(str)
  355. }
  356. return res
  357. } else {
  358. res, _ := json.Marshal(value)
  359. return res
  360. }
  361. }
  362. func (s *Secret) getField(key string) ([]byte, error) {
  363. for _, field := range s.Fields {
  364. if field.Type == key && field.Type != keeperSecurityFileRef && field.Type != keeperSecurityMfa && len(field.Value) > 0 {
  365. return getFieldValue(field.Value), nil
  366. }
  367. }
  368. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  369. }
  370. func (s *Secret) getFields() map[string][]byte {
  371. secretData := make(map[string][]byte)
  372. for _, field := range s.Fields {
  373. if len(field.Value) > 0 {
  374. secretData[field.Type] = getFieldValue(field.Value)
  375. }
  376. }
  377. return secretData
  378. }
  379. func (s *Secret) getCustomField(key string) ([]byte, error) {
  380. for _, field := range s.Custom {
  381. if field.Label == key && len(field.Value) > 0 {
  382. return getFieldValue(field.Value), nil
  383. }
  384. }
  385. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  386. }
  387. func (s *Secret) getCustomFields() map[string][]byte {
  388. secretData := make(map[string][]byte)
  389. for _, field := range s.Custom {
  390. if len(field.Value) > 0 {
  391. secretData[field.Label] = getFieldValue(field.Value)
  392. }
  393. }
  394. return secretData
  395. }
  396. func (s *Secret) getFile(key string) ([]byte, error) {
  397. for _, file := range s.Files {
  398. if file.Title == key {
  399. return []byte(file.Content), nil
  400. }
  401. }
  402. return nil, fmt.Errorf(errKeeperSecurityInvalidField, key)
  403. }
  404. func (s *Secret) getProperty(key string) ([]byte, error) {
  405. field, _ := s.getField(key)
  406. if field != nil {
  407. return field, nil
  408. }
  409. customField, _ := s.getCustomField(key)
  410. if customField != nil {
  411. return customField, nil
  412. }
  413. file, _ := s.getFile(key)
  414. if file != nil {
  415. return file, nil
  416. }
  417. return nil, fmt.Errorf(errKeeperSecurityInvalidProperty, s.Title, key)
  418. }
  419. func (s *Secret) getFiles() map[string][]byte {
  420. secretData := make(map[string][]byte)
  421. for _, file := range s.Files {
  422. secretData[file.Title] = []byte(file.Content)
  423. }
  424. return secretData
  425. }
  426. func (s *Secret) toString() (string, error) {
  427. secretJSON, err := json.Marshal(s)
  428. if err != nil {
  429. return "", fmt.Errorf(errInvalidJSONSecret, s.Title, err)
  430. }
  431. return string(secretJSON), nil
  432. }