server.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package store
  14. import (
  15. "context"
  16. "fmt"
  17. "strings"
  18. corev1 "k8s.io/api/core/v1"
  19. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/runtime/schema"
  22. "sigs.k8s.io/controller-runtime/pkg/client"
  23. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  24. pb "github.com/external-secrets/external-secrets/proto/provider"
  25. )
  26. const readIdentityRequiredErr = "provider reference or compatibility store is required for read operations"
  27. // Server wraps v1 providers and generators and exposes them as v2 gRPC services.
  28. // This allows existing v1 provider and generator implementations to be used in the v2 architecture.
  29. type Server struct {
  30. pb.UnimplementedSecretStoreProviderServer
  31. kubeClient client.Client
  32. // we support multiple v1 providers, so we need to map the v2 provider
  33. // with apiVersion+kind to the corresponding v1 provider
  34. resourceMapping ProviderMapping
  35. specMapper SpecMapper
  36. // compatibilityProvider handles read requests that carry a serialized v1 SecretStore payload.
  37. compatibilityProvider esv1.ProviderInterface
  38. }
  39. // ProviderMapping maps Kubernetes resources to their provider implementations.
  40. type ProviderMapping map[schema.GroupVersionKind]esv1.ProviderInterface
  41. // SpecMapper maps a provider reference to a SecretStoreSpec.
  42. // This is used to create a synthetic store for the v1 provider.
  43. type SpecMapper func(ref *pb.ProviderReference, sourceNamespace string) (*esv1.SecretStoreSpec, error)
  44. // NewServer creates a new AdapterServer that wraps v1 providers and generators.
  45. func NewServer(kubeClient client.Client, resourceMapping ProviderMapping, specMapping SpecMapper) *Server {
  46. return NewServerWithCompatibilityProvider(kubeClient, resourceMapping, specMapping, nil)
  47. }
  48. // NewServerWithCompatibilityProvider creates a new AdapterServer with an explicit provider
  49. // for compatibility-store based read requests.
  50. func NewServerWithCompatibilityProvider(
  51. kubeClient client.Client,
  52. resourceMapping ProviderMapping,
  53. specMapping SpecMapper,
  54. compatibilityProvider esv1.ProviderInterface,
  55. ) *Server {
  56. return &Server{
  57. kubeClient: kubeClient,
  58. resourceMapping: resourceMapping,
  59. specMapper: specMapping,
  60. compatibilityProvider: compatibilityProvider,
  61. }
  62. }
  63. func (s *Server) resolveProvider(ref *pb.ProviderReference) (esv1.ProviderInterface, error) {
  64. if ref == nil {
  65. return nil, fmt.Errorf("provider reference is nil")
  66. }
  67. splitted := strings.Split(ref.ApiVersion, "/")
  68. if len(splitted) != 2 {
  69. return nil, fmt.Errorf("invalid api version: %s", ref.ApiVersion)
  70. }
  71. group := splitted[0]
  72. version := splitted[1]
  73. key := schema.GroupVersionKind{
  74. Group: group,
  75. Version: version,
  76. Kind: ref.Kind,
  77. }
  78. v1Provider, ok := s.resourceMapping[key]
  79. if !ok {
  80. return nil, fmt.Errorf("resource mapping not found for %q", key)
  81. }
  82. return v1Provider, nil
  83. }
  84. func (s *Server) getClient(ctx context.Context, ref *pb.ProviderReference, namespace string) (esv1.SecretsClient, error) {
  85. if ref == nil {
  86. return nil, fmt.Errorf("provider reference is required")
  87. }
  88. spec, err := s.specMapper(ref, namespace)
  89. if err != nil {
  90. return nil, fmt.Errorf("failed to map provider reference to spec: %w", err)
  91. }
  92. // namespace is the resolved authentication namespace (from Provider/ClusterProvider if applicable)
  93. syntheticStore, err := NewSyntheticStore(spec, namespace, ref.GetStoreRefKind())
  94. if err != nil {
  95. return nil, fmt.Errorf("failed to create synthetic store: %w", err)
  96. }
  97. provider, err := s.resolveProvider(ref)
  98. if err != nil {
  99. return nil, fmt.Errorf("failed to resolve provider: %w", err)
  100. }
  101. clientNamespace := namespace
  102. if ref.GetNamespace() != "" {
  103. clientNamespace = ref.GetNamespace()
  104. }
  105. syntheticStore.Namespace = clientNamespace
  106. return provider.NewClient(ctx, syntheticStore, s.kubeClient, clientNamespace)
  107. }
  108. func (s *Server) getCompatibilityClient(ctx context.Context, store *pb.CompatibilityStore, namespace string) (esv1.SecretsClient, error) {
  109. if s.compatibilityProvider == nil {
  110. return nil, fmt.Errorf("compatibility provider is not configured")
  111. }
  112. syntheticStore, err := CompatibilityStoreToSyntheticStore(store)
  113. if err != nil {
  114. return nil, fmt.Errorf("failed to create synthetic store from compatibility payload: %w", err)
  115. }
  116. return s.compatibilityProvider.NewClient(ctx, syntheticStore, s.kubeClient, namespace)
  117. }
  118. func (s *Server) getReadClient(
  119. ctx context.Context,
  120. providerRef *pb.ProviderReference,
  121. compatibilityStore *pb.CompatibilityStore,
  122. sourceNamespace string,
  123. ) (esv1.SecretsClient, error) {
  124. if err := validateReadIdentity(providerRef, compatibilityStore); err != nil {
  125. return nil, err
  126. }
  127. if compatibilityStore != nil {
  128. return s.getCompatibilityClient(ctx, compatibilityStore, sourceNamespace)
  129. }
  130. return s.getClient(ctx, providerRef, sourceNamespace)
  131. }
  132. func validateReadIdentity(providerRef *pb.ProviderReference, compatibilityStore *pb.CompatibilityStore) error {
  133. if providerRef == nil && compatibilityStore == nil {
  134. return fmt.Errorf(readIdentityRequiredErr)
  135. }
  136. return nil
  137. }
  138. func (s *Server) getClientForRemoteRef(
  139. ctx context.Context,
  140. providerRef *pb.ProviderReference,
  141. compatibilityStore *pb.CompatibilityStore,
  142. sourceNamespace string,
  143. remoteRef *pb.ExternalSecretDataRemoteRef,
  144. ) (esv1.SecretsClient, esv1.ExternalSecretDataRemoteRef, error) {
  145. if remoteRef == nil {
  146. return nil, esv1.ExternalSecretDataRemoteRef{}, fmt.Errorf("request or remote ref is nil")
  147. }
  148. if err := validateSourceNamespace(sourceNamespace); err != nil {
  149. return nil, esv1.ExternalSecretDataRemoteRef{}, err
  150. }
  151. client, err := s.getReadClient(ctx, providerRef, compatibilityStore, sourceNamespace)
  152. if err != nil {
  153. if err.Error() == readIdentityRequiredErr {
  154. return nil, esv1.ExternalSecretDataRemoteRef{}, err
  155. }
  156. return nil, esv1.ExternalSecretDataRemoteRef{}, fmt.Errorf("failed to get client: %w", err)
  157. }
  158. ref := esv1.ExternalSecretDataRemoteRef{
  159. Key: remoteRef.Key,
  160. Version: remoteRef.Version,
  161. Property: remoteRef.Property,
  162. }
  163. if remoteRef.DecodingStrategy != "" {
  164. ref.DecodingStrategy = esv1.ExternalSecretDecodingStrategy(remoteRef.DecodingStrategy)
  165. }
  166. if remoteRef.MetadataPolicy != "" {
  167. ref.MetadataPolicy = esv1.ExternalSecretMetadataPolicy(remoteRef.MetadataPolicy)
  168. }
  169. return client, ref, nil
  170. }
  171. // GetSecret retrieves a single secret from the provider.
  172. func (s *Server) GetSecret(ctx context.Context, req *pb.GetSecretRequest) (*pb.GetSecretResponse, error) {
  173. if req == nil {
  174. return nil, fmt.Errorf("request or remote ref is nil")
  175. }
  176. client, ref, err := s.getClientForRemoteRef(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace, req.RemoteRef)
  177. if err != nil {
  178. return nil, err
  179. }
  180. defer func() { _ = client.Close(ctx) }()
  181. value, err := client.GetSecret(ctx, ref)
  182. if err != nil {
  183. return nil, fmt.Errorf("failed to get secret: %w", err)
  184. }
  185. return &pb.GetSecretResponse{
  186. Value: value,
  187. }, nil
  188. }
  189. // GetSecretMap retrieves multiple key/value pairs from a single secret object.
  190. func (s *Server) GetSecretMap(ctx context.Context, req *pb.GetSecretMapRequest) (*pb.GetSecretMapResponse, error) {
  191. if req == nil {
  192. return nil, fmt.Errorf("request or remote ref is nil")
  193. }
  194. client, ref, err := s.getClientForRemoteRef(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace, req.RemoteRef)
  195. if err != nil {
  196. return nil, err
  197. }
  198. defer func() { _ = client.Close(ctx) }()
  199. secrets, err := client.GetSecretMap(ctx, ref)
  200. if err != nil {
  201. return nil, fmt.Errorf("failed to get secret map: %w", err)
  202. }
  203. return &pb.GetSecretMapResponse{
  204. Secrets: secrets,
  205. }, nil
  206. }
  207. // PushSecret writes a secret to the provider.
  208. func (s *Server) PushSecret(ctx context.Context, req *pb.PushSecretRequest) (*pb.PushSecretResponse, error) {
  209. if req == nil || req.PushSecretData == nil {
  210. return nil, fmt.Errorf("request or push secret data is nil")
  211. }
  212. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  213. return nil, err
  214. }
  215. client, err := s.getReadClient(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace)
  216. if err != nil {
  217. return nil, fmt.Errorf("failed to get client: %w", err)
  218. }
  219. defer func() { _ = client.Close(ctx) }()
  220. // Convert map[string][]byte to *corev1.Secret
  221. secret := &corev1.Secret{
  222. Data: req.SecretData,
  223. Type: corev1.SecretType(req.SecretType),
  224. ObjectMeta: metav1.ObjectMeta{
  225. Labels: req.SecretLabels,
  226. Annotations: req.SecretAnnotations,
  227. },
  228. }
  229. // Convert protobuf PushSecretData to v1 PushSecretData
  230. pushData := &pushSecretData{
  231. property: req.PushSecretData.Property,
  232. secretKey: req.PushSecretData.SecretKey,
  233. remoteKey: req.PushSecretData.RemoteKey,
  234. metadata: req.PushSecretData.Metadata,
  235. }
  236. // Call v1 PushSecret
  237. if err := client.PushSecret(ctx, secret, pushData); err != nil {
  238. return nil, fmt.Errorf("failed to push secret: %w", err)
  239. }
  240. return &pb.PushSecretResponse{}, nil
  241. }
  242. // DeleteSecret deletes a secret from the provider.
  243. func (s *Server) DeleteSecret(ctx context.Context, req *pb.DeleteSecretRequest) (*pb.DeleteSecretResponse, error) {
  244. if req == nil || req.RemoteRef == nil {
  245. return nil, fmt.Errorf("request or remote ref is nil")
  246. }
  247. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  248. return nil, err
  249. }
  250. client, err := s.getReadClient(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace)
  251. if err != nil {
  252. return nil, fmt.Errorf("failed to get client: %w", err)
  253. }
  254. defer func() { _ = client.Close(ctx) }()
  255. // Convert protobuf remote ref to v1 PushSecretRemoteRef
  256. remoteRef := &pushSecretRemoteRef{
  257. remoteKey: req.RemoteRef.RemoteKey,
  258. property: req.RemoteRef.Property,
  259. }
  260. // Call v1 DeleteSecret
  261. if err := client.DeleteSecret(ctx, remoteRef); err != nil {
  262. return nil, fmt.Errorf("failed to delete secret: %w", err)
  263. }
  264. return &pb.DeleteSecretResponse{}, nil
  265. }
  266. // SecretExists checks if a secret exists in the provider.
  267. func (s *Server) SecretExists(ctx context.Context, req *pb.SecretExistsRequest) (*pb.SecretExistsResponse, error) {
  268. if req == nil || req.RemoteRef == nil {
  269. return nil, fmt.Errorf("request or remote ref is nil")
  270. }
  271. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  272. return nil, err
  273. }
  274. client, err := s.getReadClient(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace)
  275. if err != nil {
  276. return nil, fmt.Errorf("failed to get client: %w", err)
  277. }
  278. defer func() { _ = client.Close(ctx) }()
  279. // Convert protobuf remote ref to v1 PushSecretRemoteRef
  280. remoteRef := &pushSecretRemoteRef{
  281. remoteKey: req.RemoteRef.RemoteKey,
  282. property: req.RemoteRef.Property,
  283. }
  284. // Call v1 SecretExists
  285. exists, err := client.SecretExists(ctx, remoteRef)
  286. if err != nil {
  287. return nil, fmt.Errorf("failed to check if secret exists: %w", err)
  288. }
  289. return &pb.SecretExistsResponse{
  290. Exists: exists,
  291. }, nil
  292. }
  293. // GetAllSecrets retrieves multiple secrets from the provider.
  294. func (s *Server) GetAllSecrets(ctx context.Context, req *pb.GetAllSecretsRequest) (*pb.GetAllSecretsResponse, error) {
  295. if req == nil || req.Find == nil {
  296. return nil, fmt.Errorf("request or find criteria is nil")
  297. }
  298. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  299. return nil, err
  300. }
  301. client, err := s.getReadClient(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace)
  302. if err != nil {
  303. if err.Error() == readIdentityRequiredErr {
  304. return nil, err
  305. }
  306. return nil, fmt.Errorf("failed to get client: %w", err)
  307. }
  308. defer func() { _ = client.Close(ctx) }()
  309. // Convert protobuf ExternalSecretFind to v1 ExternalSecretFind
  310. find := esv1.ExternalSecretFind{
  311. Tags: req.Find.Tags,
  312. ConversionStrategy: esv1.ExternalSecretConversionStrategy(req.Find.ConversionStrategy),
  313. DecodingStrategy: esv1.ExternalSecretDecodingStrategy(req.Find.DecodingStrategy),
  314. }
  315. // Convert Path from string to *string
  316. if req.Find.Path != "" {
  317. path := req.Find.Path
  318. find.Path = &path
  319. }
  320. if req.Find.Name != nil {
  321. find.Name = &esv1.FindName{
  322. RegExp: req.Find.Name.Regexp,
  323. }
  324. }
  325. // Call v1 GetAllSecrets
  326. secrets, err := client.GetAllSecrets(ctx, find)
  327. if err != nil {
  328. return nil, fmt.Errorf("failed to get all secrets: %w", err)
  329. }
  330. return &pb.GetAllSecretsResponse{
  331. Secrets: secrets,
  332. }, nil
  333. }
  334. // Validate checks if the provider configuration is valid.
  335. func (s *Server) Validate(ctx context.Context, req *pb.ValidateRequest) (*pb.ValidateResponse, error) {
  336. if req == nil {
  337. return nil, fmt.Errorf("request is nil")
  338. }
  339. client, err := s.getReadClient(ctx, req.ProviderRef, req.CompatibilityStore, req.SourceNamespace)
  340. if err != nil {
  341. return nil, fmt.Errorf("failed to get client: %w", err)
  342. }
  343. defer func() { _ = client.Close(ctx) }()
  344. result, err := client.Validate()
  345. if err != nil {
  346. return &pb.ValidateResponse{
  347. Valid: false,
  348. Error: err.Error(),
  349. }, nil
  350. }
  351. var valid bool
  352. switch result {
  353. case esv1.ValidationResultReady:
  354. valid = true
  355. case esv1.ValidationResultUnknown:
  356. valid = true // Unknown is treated as valid but warns
  357. case esv1.ValidationResultError:
  358. valid = false
  359. }
  360. return &pb.ValidateResponse{
  361. Valid: valid,
  362. Warnings: []string{},
  363. }, nil
  364. }
  365. // Capabilities returns the capabilities of the provider.
  366. // TODO: remove / rewrite capabilities:
  367. // the provider should advertise what providers/generators it supports.
  368. func (s *Server) Capabilities(_ context.Context, req *pb.CapabilitiesRequest) (*pb.CapabilitiesResponse, error) {
  369. if req == nil {
  370. return nil, fmt.Errorf("request is nil")
  371. }
  372. provider, err := s.resolveProvider(req.ProviderRef)
  373. if err != nil {
  374. return nil, fmt.Errorf("failed to resolve provider: %w", err)
  375. }
  376. caps := provider.Capabilities()
  377. var pbCaps pb.SecretStoreCapabilities
  378. switch caps {
  379. case esv1.SecretStoreReadOnly:
  380. pbCaps = pb.SecretStoreCapabilities_READ_ONLY
  381. case esv1.SecretStoreWriteOnly:
  382. pbCaps = pb.SecretStoreCapabilities_WRITE_ONLY
  383. case esv1.SecretStoreReadWrite:
  384. pbCaps = pb.SecretStoreCapabilities_READ_WRITE
  385. default:
  386. pbCaps = pb.SecretStoreCapabilities_READ_ONLY
  387. }
  388. return &pb.CapabilitiesResponse{
  389. Capabilities: pbCaps,
  390. }, nil
  391. }
  392. func validateSourceNamespace(sourceNamespace string) error {
  393. if sourceNamespace == "" {
  394. return fmt.Errorf("source namespace is required")
  395. }
  396. return nil
  397. }
  398. // pushSecretData implements esv1.PushSecretData.
  399. type pushSecretData struct {
  400. property string
  401. secretKey string
  402. remoteKey string
  403. metadata []byte
  404. }
  405. func (p *pushSecretData) GetProperty() string {
  406. return p.property
  407. }
  408. func (p *pushSecretData) GetSecretKey() string {
  409. return p.secretKey
  410. }
  411. func (p *pushSecretData) GetRemoteKey() string {
  412. return p.remoteKey
  413. }
  414. func (p *pushSecretData) GetMetadata() *apiextensionsv1.JSON {
  415. if len(p.metadata) == 0 {
  416. return nil
  417. }
  418. return &apiextensionsv1.JSON{
  419. Raw: p.metadata,
  420. }
  421. }
  422. // pushSecretRemoteRef implements esv1.PushSecretRemoteRef.
  423. type pushSecretRemoteRef struct {
  424. remoteKey string
  425. property string
  426. }
  427. func (p *pushSecretRemoteRef) GetRemoteKey() string {
  428. return p.remoteKey
  429. }
  430. func (p *pushSecretRemoteRef) GetProperty() string {
  431. return p.property
  432. }