server.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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. // Server wraps v1 providers and generators and exposes them as v2 gRPC services.
  27. // This allows existing v1 provider and generator implementations to be used in the v2 architecture.
  28. type Server struct {
  29. pb.UnimplementedSecretStoreProviderServer
  30. kubeClient client.Client
  31. // we support multiple v1 providers, so we need to map the v2 provider
  32. // with apiVersion+kind to the corresponding v1 provider
  33. resourceMapping ProviderMapping
  34. specMapper SpecMapper
  35. }
  36. // ProviderMapping maps Kubernetes resources to their provider implementations.
  37. type ProviderMapping map[schema.GroupVersionKind]esv1.ProviderInterface
  38. // SpecMapper maps a provider reference to a SecretStoreSpec.
  39. // This is used to create a synthetic store for the v1 provider.
  40. type SpecMapper func(ref *pb.ProviderReference, sourceNamespace string) (*esv1.SecretStoreSpec, error)
  41. // NewServer creates a new AdapterServer that wraps v1 providers and generators.
  42. func NewServer(kubeClient client.Client, resourceMapping ProviderMapping, specMapping SpecMapper) *Server {
  43. return &Server{
  44. kubeClient: kubeClient,
  45. resourceMapping: resourceMapping,
  46. specMapper: specMapping,
  47. }
  48. }
  49. func (s *Server) resolveProvider(ref *pb.ProviderReference) (esv1.ProviderInterface, error) {
  50. if ref == nil {
  51. return nil, fmt.Errorf("provider reference is nil")
  52. }
  53. splitted := strings.Split(ref.ApiVersion, "/")
  54. if len(splitted) != 2 {
  55. return nil, fmt.Errorf("invalid api version: %s", ref.ApiVersion)
  56. }
  57. group := splitted[0]
  58. version := splitted[1]
  59. key := schema.GroupVersionKind{
  60. Group: group,
  61. Version: version,
  62. Kind: ref.Kind,
  63. }
  64. v1Provider, ok := s.resourceMapping[key]
  65. if !ok {
  66. return nil, fmt.Errorf("resource mapping not found for %q", key)
  67. }
  68. return v1Provider, nil
  69. }
  70. func (s *Server) getClient(ctx context.Context, ref *pb.ProviderReference, namespace string) (esv1.SecretsClient, error) {
  71. if ref == nil {
  72. return nil, fmt.Errorf("request or remote ref is nil")
  73. }
  74. spec, err := s.specMapper(ref, namespace)
  75. if err != nil {
  76. return nil, fmt.Errorf("failed to map provider reference to spec: %w", err)
  77. }
  78. // namespace is the resolved authentication namespace (from Provider/ClusterProvider if applicable)
  79. syntheticStore, err := NewSyntheticStore(spec, namespace, ref.GetStoreRefKind())
  80. if err != nil {
  81. return nil, fmt.Errorf("failed to create synthetic store: %w", err)
  82. }
  83. provider, err := s.resolveProvider(ref)
  84. if err != nil {
  85. return nil, fmt.Errorf("failed to resolve provider: %w", err)
  86. }
  87. return provider.NewClient(ctx, syntheticStore, s.kubeClient, namespace)
  88. }
  89. func (s *Server) getClientForRemoteRef(
  90. ctx context.Context,
  91. providerRef *pb.ProviderReference,
  92. sourceNamespace string,
  93. remoteRef *pb.ExternalSecretDataRemoteRef,
  94. ) (esv1.SecretsClient, esv1.ExternalSecretDataRemoteRef, error) {
  95. if remoteRef == nil {
  96. return nil, esv1.ExternalSecretDataRemoteRef{}, fmt.Errorf("request or remote ref is nil")
  97. }
  98. if err := validateSourceNamespace(sourceNamespace); err != nil {
  99. return nil, esv1.ExternalSecretDataRemoteRef{}, err
  100. }
  101. client, err := s.getClient(ctx, providerRef, sourceNamespace)
  102. if err != nil {
  103. return nil, esv1.ExternalSecretDataRemoteRef{}, fmt.Errorf("failed to get client: %w", err)
  104. }
  105. ref := esv1.ExternalSecretDataRemoteRef{
  106. Key: remoteRef.Key,
  107. Version: remoteRef.Version,
  108. Property: remoteRef.Property,
  109. }
  110. if remoteRef.DecodingStrategy != "" {
  111. ref.DecodingStrategy = esv1.ExternalSecretDecodingStrategy(remoteRef.DecodingStrategy)
  112. }
  113. if remoteRef.MetadataPolicy != "" {
  114. ref.MetadataPolicy = esv1.ExternalSecretMetadataPolicy(remoteRef.MetadataPolicy)
  115. }
  116. return client, ref, nil
  117. }
  118. // GetSecret retrieves a single secret from the provider.
  119. func (s *Server) GetSecret(ctx context.Context, req *pb.GetSecretRequest) (*pb.GetSecretResponse, error) {
  120. if req == nil {
  121. return nil, fmt.Errorf("request or remote ref is nil")
  122. }
  123. client, ref, err := s.getClientForRemoteRef(ctx, req.ProviderRef, req.SourceNamespace, req.RemoteRef)
  124. if err != nil {
  125. return nil, err
  126. }
  127. defer func() { _ = client.Close(ctx) }()
  128. value, err := client.GetSecret(ctx, ref)
  129. if err != nil {
  130. return nil, fmt.Errorf("failed to get secret: %w", err)
  131. }
  132. return &pb.GetSecretResponse{
  133. Value: value,
  134. }, nil
  135. }
  136. // GetSecretMap retrieves multiple key/value pairs from a single secret object.
  137. func (s *Server) GetSecretMap(ctx context.Context, req *pb.GetSecretMapRequest) (*pb.GetSecretMapResponse, error) {
  138. if req == nil {
  139. return nil, fmt.Errorf("request or remote ref is nil")
  140. }
  141. client, ref, err := s.getClientForRemoteRef(ctx, req.ProviderRef, req.SourceNamespace, req.RemoteRef)
  142. if err != nil {
  143. return nil, err
  144. }
  145. defer func() { _ = client.Close(ctx) }()
  146. secrets, err := client.GetSecretMap(ctx, ref)
  147. if err != nil {
  148. return nil, fmt.Errorf("failed to get secret map: %w", err)
  149. }
  150. return &pb.GetSecretMapResponse{
  151. Secrets: secrets,
  152. }, nil
  153. }
  154. // PushSecret writes a secret to the provider.
  155. func (s *Server) PushSecret(ctx context.Context, req *pb.PushSecretRequest) (*pb.PushSecretResponse, error) {
  156. if req == nil || req.PushSecretData == nil {
  157. return nil, fmt.Errorf("request or push secret data is nil")
  158. }
  159. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  160. return nil, err
  161. }
  162. client, err := s.getClient(ctx, req.ProviderRef, req.SourceNamespace)
  163. if err != nil {
  164. return nil, fmt.Errorf("failed to get client: %w", err)
  165. }
  166. defer func() { _ = client.Close(ctx) }()
  167. // Convert map[string][]byte to *corev1.Secret
  168. secret := &corev1.Secret{
  169. Data: req.SecretData,
  170. Type: corev1.SecretType(req.SecretType),
  171. ObjectMeta: metav1.ObjectMeta{
  172. Labels: req.SecretLabels,
  173. Annotations: req.SecretAnnotations,
  174. },
  175. }
  176. // Convert protobuf PushSecretData to v1 PushSecretData
  177. pushData := &pushSecretData{
  178. property: req.PushSecretData.Property,
  179. secretKey: req.PushSecretData.SecretKey,
  180. remoteKey: req.PushSecretData.RemoteKey,
  181. metadata: req.PushSecretData.Metadata,
  182. }
  183. // Call v1 PushSecret
  184. if err := client.PushSecret(ctx, secret, pushData); err != nil {
  185. return nil, fmt.Errorf("failed to push secret: %w", err)
  186. }
  187. return &pb.PushSecretResponse{}, nil
  188. }
  189. // DeleteSecret deletes a secret from the provider.
  190. func (s *Server) DeleteSecret(ctx context.Context, req *pb.DeleteSecretRequest) (*pb.DeleteSecretResponse, error) {
  191. if req == nil || req.RemoteRef == nil {
  192. return nil, fmt.Errorf("request or remote ref is nil")
  193. }
  194. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  195. return nil, err
  196. }
  197. client, err := s.getClient(ctx, req.ProviderRef, req.SourceNamespace)
  198. if err != nil {
  199. return nil, fmt.Errorf("failed to get client: %w", err)
  200. }
  201. defer func() { _ = client.Close(ctx) }()
  202. // Convert protobuf remote ref to v1 PushSecretRemoteRef
  203. remoteRef := &pushSecretRemoteRef{
  204. remoteKey: req.RemoteRef.RemoteKey,
  205. property: req.RemoteRef.Property,
  206. }
  207. // Call v1 DeleteSecret
  208. if err := client.DeleteSecret(ctx, remoteRef); err != nil {
  209. return nil, fmt.Errorf("failed to delete secret: %w", err)
  210. }
  211. return &pb.DeleteSecretResponse{}, nil
  212. }
  213. // SecretExists checks if a secret exists in the provider.
  214. func (s *Server) SecretExists(ctx context.Context, req *pb.SecretExistsRequest) (*pb.SecretExistsResponse, error) {
  215. if req == nil || req.RemoteRef == nil {
  216. return nil, fmt.Errorf("request or remote ref is nil")
  217. }
  218. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  219. return nil, err
  220. }
  221. client, err := s.getClient(ctx, req.ProviderRef, req.SourceNamespace)
  222. if err != nil {
  223. return nil, fmt.Errorf("failed to get client: %w", err)
  224. }
  225. defer func() { _ = client.Close(ctx) }()
  226. // Convert protobuf remote ref to v1 PushSecretRemoteRef
  227. remoteRef := &pushSecretRemoteRef{
  228. remoteKey: req.RemoteRef.RemoteKey,
  229. property: req.RemoteRef.Property,
  230. }
  231. // Call v1 SecretExists
  232. exists, err := client.SecretExists(ctx, remoteRef)
  233. if err != nil {
  234. return nil, fmt.Errorf("failed to check if secret exists: %w", err)
  235. }
  236. return &pb.SecretExistsResponse{
  237. Exists: exists,
  238. }, nil
  239. }
  240. // GetAllSecrets retrieves multiple secrets from the provider.
  241. func (s *Server) GetAllSecrets(ctx context.Context, req *pb.GetAllSecretsRequest) (*pb.GetAllSecretsResponse, error) {
  242. if req == nil || req.Find == nil {
  243. return nil, fmt.Errorf("request or find criteria is nil")
  244. }
  245. if err := validateSourceNamespace(req.SourceNamespace); err != nil {
  246. return nil, err
  247. }
  248. client, err := s.getClient(ctx, req.ProviderRef, req.SourceNamespace)
  249. if err != nil {
  250. return nil, fmt.Errorf("failed to get client: %w", err)
  251. }
  252. defer func() { _ = client.Close(ctx) }()
  253. // Convert protobuf ExternalSecretFind to v1 ExternalSecretFind
  254. find := esv1.ExternalSecretFind{
  255. Tags: req.Find.Tags,
  256. ConversionStrategy: esv1.ExternalSecretConversionStrategy(req.Find.ConversionStrategy),
  257. DecodingStrategy: esv1.ExternalSecretDecodingStrategy(req.Find.DecodingStrategy),
  258. }
  259. // Convert Path from string to *string
  260. if req.Find.Path != "" {
  261. path := req.Find.Path
  262. find.Path = &path
  263. }
  264. if req.Find.Name != nil {
  265. find.Name = &esv1.FindName{
  266. RegExp: req.Find.Name.Regexp,
  267. }
  268. }
  269. // Call v1 GetAllSecrets
  270. secrets, err := client.GetAllSecrets(ctx, find)
  271. if err != nil {
  272. return nil, fmt.Errorf("failed to get all secrets: %w", err)
  273. }
  274. return &pb.GetAllSecretsResponse{
  275. Secrets: secrets,
  276. }, nil
  277. }
  278. // Validate checks if the provider configuration is valid.
  279. func (s *Server) Validate(ctx context.Context, req *pb.ValidateRequest) (*pb.ValidateResponse, error) {
  280. if req == nil {
  281. return nil, fmt.Errorf("request is nil")
  282. }
  283. client, err := s.getClient(ctx, req.ProviderRef, req.SourceNamespace)
  284. if err != nil {
  285. return nil, fmt.Errorf("failed to get client: %w", err)
  286. }
  287. defer func() { _ = client.Close(ctx) }()
  288. result, err := client.Validate()
  289. if err != nil {
  290. return &pb.ValidateResponse{
  291. Valid: false,
  292. Error: err.Error(),
  293. }, nil
  294. }
  295. var valid bool
  296. switch result {
  297. case esv1.ValidationResultReady:
  298. valid = true
  299. case esv1.ValidationResultUnknown:
  300. valid = true // Unknown is treated as valid but warns
  301. case esv1.ValidationResultError:
  302. valid = false
  303. }
  304. return &pb.ValidateResponse{
  305. Valid: valid,
  306. Warnings: []string{},
  307. }, nil
  308. }
  309. // Capabilities returns the capabilities of the provider.
  310. // TODO: remove / rewrite capabilities:
  311. // the provider should advertise what providers/generators it supports.
  312. func (s *Server) Capabilities(_ context.Context, req *pb.CapabilitiesRequest) (*pb.CapabilitiesResponse, error) {
  313. if req == nil {
  314. return nil, fmt.Errorf("request is nil")
  315. }
  316. provider, err := s.resolveProvider(req.ProviderRef)
  317. if err != nil {
  318. return nil, fmt.Errorf("failed to resolve provider: %w", err)
  319. }
  320. caps := provider.Capabilities()
  321. var pbCaps pb.SecretStoreCapabilities
  322. switch caps {
  323. case esv1.SecretStoreReadOnly:
  324. pbCaps = pb.SecretStoreCapabilities_READ_ONLY
  325. case esv1.SecretStoreWriteOnly:
  326. pbCaps = pb.SecretStoreCapabilities_WRITE_ONLY
  327. case esv1.SecretStoreReadWrite:
  328. pbCaps = pb.SecretStoreCapabilities_READ_WRITE
  329. default:
  330. pbCaps = pb.SecretStoreCapabilities_READ_ONLY
  331. }
  332. return &pb.CapabilitiesResponse{
  333. Capabilities: pbCaps,
  334. }, nil
  335. }
  336. func validateSourceNamespace(sourceNamespace string) error {
  337. if sourceNamespace == "" {
  338. return fmt.Errorf("source namespace is required")
  339. }
  340. return nil
  341. }
  342. // pushSecretData implements esv1.PushSecretData.
  343. type pushSecretData struct {
  344. property string
  345. secretKey string
  346. remoteKey string
  347. metadata []byte
  348. }
  349. func (p *pushSecretData) GetProperty() string {
  350. return p.property
  351. }
  352. func (p *pushSecretData) GetSecretKey() string {
  353. return p.secretKey
  354. }
  355. func (p *pushSecretData) GetRemoteKey() string {
  356. return p.remoteKey
  357. }
  358. func (p *pushSecretData) GetMetadata() *apiextensionsv1.JSON {
  359. if len(p.metadata) == 0 {
  360. return nil
  361. }
  362. return &apiextensionsv1.JSON{
  363. Raw: p.metadata,
  364. }
  365. }
  366. // pushSecretRemoteRef implements esv1.PushSecretRemoteRef.
  367. type pushSecretRemoteRef struct {
  368. remoteKey string
  369. property string
  370. }
  371. func (p *pushSecretRemoteRef) GetRemoteKey() string {
  372. return p.remoteKey
  373. }
  374. func (p *pushSecretRemoteRef) GetProperty() string {
  375. return p.property
  376. }