server.go 13 KB

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