mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
a5ddd97c21
* chore: update go version of the project to 1.23 Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> * fixed an absurd amount of linter issues Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> --------- Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
715 lines
22 KiB
Go
715 lines
22 KiB
Go
/*
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package ibm
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/IBM/go-sdk-core/v5/core"
|
|
sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2"
|
|
"github.com/google/uuid"
|
|
"github.com/tidwall/gjson"
|
|
corev1 "k8s.io/api/core/v1"
|
|
kclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
|
|
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
|
"github.com/external-secrets/external-secrets/pkg/constants"
|
|
"github.com/external-secrets/external-secrets/pkg/metrics"
|
|
"github.com/external-secrets/external-secrets/pkg/utils"
|
|
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
|
|
)
|
|
|
|
const (
|
|
SecretsManagerEndpointEnv = "IBM_SECRETSMANAGER_ENDPOINT"
|
|
STSEndpointEnv = "IBM_STS_ENDPOINT"
|
|
SSMEndpointEnv = "IBM_SSM_ENDPOINT"
|
|
|
|
certificateConst = "certificate"
|
|
intermediateConst = "intermediate"
|
|
privateKeyConst = "private_key"
|
|
usernameConst = "username"
|
|
passwordConst = "password"
|
|
apikeyConst = "apikey"
|
|
credentialsConst = "credentials"
|
|
arbitraryConst = "arbitrary"
|
|
payloadConst = "payload"
|
|
smAPIKeyConst = "api_key"
|
|
|
|
errIBMClient = "cannot setup new ibm client: %w"
|
|
errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey"
|
|
errUninitalizedIBMProvider = "provider IBM is not initialized"
|
|
errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
|
|
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
|
|
errJSONSecretMarshal = "unable to marshal secret: %w"
|
|
errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
|
|
errNotImplemented = "not implemented"
|
|
)
|
|
|
|
var contextTimeout = time.Minute * 2
|
|
|
|
// https://github.com/external-secrets/external-secrets/issues/644
|
|
var (
|
|
_ esv1beta1.SecretsClient = &providerIBM{}
|
|
_ esv1beta1.Provider = &providerIBM{}
|
|
)
|
|
|
|
type SecretManagerClient interface {
|
|
GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error)
|
|
GetSecretByNameTypeWithContext(ctx context.Context, getSecretByNameTypeOptions *sm.GetSecretByNameTypeOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error)
|
|
}
|
|
|
|
type providerIBM struct {
|
|
IBMClient SecretManagerClient
|
|
}
|
|
|
|
type client struct {
|
|
kube kclient.Client
|
|
store *esv1beta1.IBMProvider
|
|
namespace string
|
|
storeKind string
|
|
credentials []byte
|
|
}
|
|
|
|
func (c *client) setAuth(ctx context.Context) error {
|
|
apiKey, err := resolvers.SecretKeyRef(ctx, c.kube, c.storeKind, c.namespace, &c.store.Auth.SecretRef.SecretAPIKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.credentials = []byte(apiKey)
|
|
return nil
|
|
}
|
|
|
|
func (ibm *providerIBM) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
|
|
return errors.New(errNotImplemented)
|
|
}
|
|
|
|
func (ibm *providerIBM) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
|
return false, errors.New(errNotImplemented)
|
|
}
|
|
|
|
// Not Implemented PushSecret.
|
|
func (ibm *providerIBM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
|
|
return errors.New(errNotImplemented)
|
|
}
|
|
|
|
// Empty GetAllSecrets.
|
|
func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
|
|
// TO be implemented
|
|
return nil, errors.New(errNotImplemented)
|
|
}
|
|
|
|
func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
|
|
if utils.IsNil(ibm.IBMClient) {
|
|
return nil, errors.New(errUninitalizedIBMProvider)
|
|
}
|
|
|
|
var secretGroupName string
|
|
secretType := sm.Secret_SecretType_Arbitrary
|
|
secretName := ref.Key
|
|
nameSplitted := strings.Split(secretName, "/")
|
|
|
|
switch len(nameSplitted) {
|
|
case 2:
|
|
secretType = nameSplitted[0]
|
|
secretName = nameSplitted[1]
|
|
case 3:
|
|
secretGroupName = nameSplitted[0]
|
|
secretType = nameSplitted[1]
|
|
secretName = nameSplitted[2]
|
|
}
|
|
|
|
switch secretType {
|
|
case sm.Secret_SecretType_Arbitrary:
|
|
return getArbitrarySecret(ibm, &secretName, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_UsernamePassword:
|
|
|
|
if ref.Property == "" {
|
|
return nil, errors.New("remoteRef.property required for secret type username_password")
|
|
}
|
|
return getUsernamePasswordSecret(ibm, &secretName, ref, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_IamCredentials:
|
|
|
|
return getIamCredentialsSecret(ibm, &secretName, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_ServiceCredentials:
|
|
|
|
return getServiceCredentialsSecret(ibm, &secretName, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_ImportedCert:
|
|
|
|
if ref.Property == "" {
|
|
return nil, errors.New("remoteRef.property required for secret type imported_cert")
|
|
}
|
|
|
|
return getImportCertSecret(ibm, &secretName, ref, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_PublicCert:
|
|
|
|
if ref.Property == "" {
|
|
return nil, errors.New("remoteRef.property required for secret type public_cert")
|
|
}
|
|
|
|
return getPublicCertSecret(ibm, &secretName, ref, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_PrivateCert:
|
|
|
|
if ref.Property == "" {
|
|
return nil, errors.New("remoteRef.property required for secret type private_cert")
|
|
}
|
|
|
|
return getPrivateCertSecret(ibm, &secretName, ref, secretGroupName)
|
|
|
|
case sm.Secret_SecretType_Kv:
|
|
|
|
response, err := getSecretData(ibm, &secretName, sm.Secret_SecretType_Kv, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secret, ok := response.(*sm.KVSecret)
|
|
if !ok {
|
|
return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecret")
|
|
}
|
|
return getKVSecret(ref, secret)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown secret type %s", secretType)
|
|
}
|
|
}
|
|
|
|
func getArbitrarySecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Arbitrary, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[payloadConst]; ok {
|
|
return []byte(val.(string)), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", payloadConst, *secretName)
|
|
}
|
|
|
|
func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ImportedCert, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
val, ok := secMap[ref.Property]
|
|
if ok {
|
|
return []byte(val.(string)), nil
|
|
} else if ref.Property == privateKeyConst {
|
|
// we want to return an empty string in case the secret doesn't contain a private key
|
|
// this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty private key
|
|
fmt.Printf("warn: %s is empty for secret %s\n", privateKeyConst, *secretName)
|
|
return []byte(""), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
}
|
|
|
|
func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PublicCert, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[ref.Property]; ok {
|
|
return []byte(val.(string)), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
}
|
|
|
|
func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PrivateCert, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[ref.Property]; ok {
|
|
return []byte(val.(string)), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
}
|
|
|
|
func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_IamCredentials, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[smAPIKeyConst]; ok {
|
|
return []byte(val.(string)), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", smAPIKeyConst, *secretName)
|
|
}
|
|
|
|
func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ServiceCredentials, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[credentialsConst]; ok {
|
|
mval, err := json.Marshal(val)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal secret map for service credentials secret: %w", err)
|
|
}
|
|
return mval, nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", credentialsConst, *secretName)
|
|
}
|
|
|
|
func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
|
|
response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_UsernamePassword, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if val, ok := secMap[ref.Property]; ok {
|
|
return []byte(val.(string)), nil
|
|
}
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
}
|
|
|
|
// Returns a secret of type kv and supports json path.
|
|
func getKVSecret(ref esv1beta1.ExternalSecretDataRemoteRef, secret *sm.KVSecret) ([]byte, error) {
|
|
payloadJSONByte, err := json.Marshal(secret.Data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshaling payload from secret failed. %w", err)
|
|
}
|
|
payloadJSON := string(payloadJSONByte)
|
|
|
|
// no property requested, return the entire payload
|
|
if ref.Property == "" {
|
|
return []byte(payloadJSON), nil
|
|
}
|
|
|
|
// returns the requested key
|
|
// consider that the key contains a ".". this could be one of 2 options
|
|
// a) "." is part of the key name
|
|
// b) "." is symbole for JSON path
|
|
if ref.Property != "" {
|
|
refProperty := ref.Property
|
|
|
|
// a) "." is part the key name
|
|
// escape "."
|
|
idx := strings.Index(refProperty, ".")
|
|
if idx > 0 {
|
|
refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
|
|
|
|
val := gjson.Get(payloadJSON, refProperty)
|
|
if val.Exists() {
|
|
return []byte(val.String()), nil
|
|
}
|
|
}
|
|
|
|
// b) "." is symbole for JSON path
|
|
// try to get value for this path
|
|
val := gjson.Get(payloadJSON, ref.Property)
|
|
if !val.Exists() {
|
|
return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
|
|
}
|
|
return []byte(val.String()), nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("no property provided for secret %s", ref.Key)
|
|
}
|
|
|
|
func getSecretData(ibm *providerIBM, secretName *string, secretType, secretGroupName string) (sm.SecretIntf, error) {
|
|
_, err := uuid.Parse(*secretName)
|
|
if err != nil {
|
|
// secret name has been provided instead of id
|
|
if secretGroupName == "" {
|
|
// secret group name is not provided
|
|
return nil, errors.New("failed to fetch the secret, secret group name is missing")
|
|
}
|
|
|
|
// secret group name is provided along with secret name,
|
|
// follow the new mechanism by calling GetSecretByNameTypeWithContext
|
|
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
|
defer cancel()
|
|
response, _, err := ibm.IBMClient.GetSecretByNameTypeWithContext(
|
|
ctx,
|
|
&sm.GetSecretByNameTypeOptions{
|
|
Name: secretName,
|
|
SecretGroupName: &secretGroupName,
|
|
SecretType: &secretType,
|
|
})
|
|
metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMGetSecretByNameType, err)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
|
defer cancel()
|
|
response, _, err := ibm.IBMClient.GetSecretWithContext(
|
|
ctx,
|
|
&sm.GetSecretOptions{
|
|
ID: secretName,
|
|
})
|
|
metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMGetSecret, err)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
|
|
if utils.IsNil(ibm.IBMClient) {
|
|
return nil, errors.New(errUninitalizedIBMProvider)
|
|
}
|
|
var secretGroupName string
|
|
secretType := sm.Secret_SecretType_Arbitrary
|
|
secretName := ref.Key
|
|
nameSplitted := strings.Split(secretName, "/")
|
|
switch len(nameSplitted) {
|
|
case 2:
|
|
secretType = nameSplitted[0]
|
|
secretName = nameSplitted[1]
|
|
case 3:
|
|
secretGroupName = nameSplitted[0]
|
|
secretType = nameSplitted[1]
|
|
secretName = nameSplitted[2]
|
|
}
|
|
|
|
secretMap := make(map[string][]byte)
|
|
secMapBytes := make(map[string][]byte)
|
|
response, err := getSecretData(ibm, &secretName, secretType, secretGroupName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
secMap, err := formSecretMap(response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
|
|
secretMap = populateSecretMap(secretMap, secMap)
|
|
}
|
|
secMapBytes = populateSecretMap(secMapBytes, secMap)
|
|
|
|
checkNilFn := func(propertyList []string) error {
|
|
for _, prop := range propertyList {
|
|
if _, ok := secMap[prop]; !ok {
|
|
return fmt.Errorf("key %s does not exist in secret %s", prop, secretName)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
switch secretType {
|
|
case sm.Secret_SecretType_Arbitrary:
|
|
if err := checkNilFn([]string{payloadConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[arbitraryConst] = secMapBytes[payloadConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_UsernamePassword:
|
|
if err := checkNilFn([]string{usernameConst, passwordConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[usernameConst] = secMapBytes[usernameConst]
|
|
secretMap[passwordConst] = secMapBytes[passwordConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_IamCredentials:
|
|
if err := checkNilFn([]string{smAPIKeyConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[apikeyConst] = secMapBytes[smAPIKeyConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_ServiceCredentials:
|
|
if err := checkNilFn([]string{credentialsConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[credentialsConst] = secMapBytes[credentialsConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_ImportedCert:
|
|
if err := checkNilFn([]string{certificateConst, intermediateConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[certificateConst] = secMapBytes[certificateConst]
|
|
secretMap[intermediateConst] = secMapBytes[intermediateConst]
|
|
if v, ok := secMapBytes[privateKeyConst]; ok {
|
|
secretMap[privateKeyConst] = v
|
|
} else {
|
|
fmt.Printf("warn: %s is empty for secret %s\n", privateKeyConst, secretName)
|
|
secretMap[privateKeyConst] = []byte("")
|
|
}
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_PublicCert:
|
|
if err := checkNilFn([]string{certificateConst, intermediateConst, privateKeyConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[certificateConst] = secMapBytes[certificateConst]
|
|
secretMap[intermediateConst] = secMapBytes[intermediateConst]
|
|
secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_PrivateCert:
|
|
if err := checkNilFn([]string{certificateConst, privateKeyConst}); err != nil {
|
|
return nil, err
|
|
}
|
|
secretMap[certificateConst] = secMapBytes[certificateConst]
|
|
secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
|
|
return secretMap, nil
|
|
|
|
case sm.Secret_SecretType_Kv:
|
|
secretData, ok := response.(*sm.KVSecret)
|
|
if !ok {
|
|
return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecretMap")
|
|
}
|
|
secret, err := getKVSecret(ref, secretData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := make(map[string]any)
|
|
err = json.Unmarshal(secret, &m)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
|
|
}
|
|
secretMap = byteArrayMap(m, secretMap)
|
|
return secretMap, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown secret type %s", secretType)
|
|
}
|
|
}
|
|
|
|
func byteArrayMap(secretData map[string]any, secretMap map[string][]byte) map[string][]byte {
|
|
var err error
|
|
for k, v := range secretData {
|
|
secretMap[k], err = utils.GetByteValue(v)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
}
|
|
return secretMap
|
|
}
|
|
|
|
func (ibm *providerIBM) Close(_ context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
func (ibm *providerIBM) Validate() (esv1beta1.ValidationResult, error) {
|
|
return esv1beta1.ValidationResultReady, nil
|
|
}
|
|
|
|
func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
|
|
storeSpec := store.GetSpec()
|
|
ibmSpec := storeSpec.Provider.IBM
|
|
if ibmSpec.ServiceURL == nil {
|
|
return nil, errors.New("serviceURL is required")
|
|
}
|
|
|
|
containerRef := ibmSpec.Auth.ContainerAuth
|
|
secretRef := ibmSpec.Auth.SecretRef
|
|
|
|
missingContainerRef := utils.IsNil(containerRef)
|
|
missingSecretRef := utils.IsNil(secretRef)
|
|
|
|
if missingContainerRef == missingSecretRef {
|
|
// since both are equal, if one is missing assume both are missing
|
|
if missingContainerRef {
|
|
return nil, errors.New("missing auth method")
|
|
}
|
|
return nil, errors.New("too many auth methods defined")
|
|
}
|
|
|
|
if !missingContainerRef {
|
|
// catch undefined container auth profile
|
|
if containerRef.Profile == "" {
|
|
return nil, errors.New("container auth profile cannot be empty")
|
|
}
|
|
|
|
// proceed with container auth
|
|
if containerRef.TokenLocation == "" {
|
|
containerRef.TokenLocation = "/var/run/secrets/tokens/vault-token"
|
|
}
|
|
if _, err := os.Open(containerRef.TokenLocation); err != nil {
|
|
return nil, fmt.Errorf("cannot read container auth token %s. %w", containerRef.TokenLocation, err)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// proceed with API Key Auth validation
|
|
secretKeyRef := secretRef.SecretAPIKey
|
|
err := utils.ValidateSecretSelector(store, secretKeyRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if secretKeyRef.Name == "" {
|
|
return nil, errors.New("secretAPIKey.name cannot be empty")
|
|
}
|
|
if secretKeyRef.Key == "" {
|
|
return nil, errors.New("secretAPIKey.key cannot be empty")
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
|
|
func (ibm *providerIBM) Capabilities() esv1beta1.SecretStoreCapabilities {
|
|
return esv1beta1.SecretStoreReadOnly
|
|
}
|
|
|
|
func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
|
|
storeSpec := store.GetSpec()
|
|
ibmSpec := storeSpec.Provider.IBM
|
|
|
|
iStore := &client{
|
|
kube: kube,
|
|
store: ibmSpec,
|
|
namespace: namespace,
|
|
storeKind: store.GetObjectKind().GroupVersionKind().Kind,
|
|
}
|
|
|
|
var err error
|
|
var secretsManager *sm.SecretsManagerV2
|
|
containerAuth := iStore.store.Auth.ContainerAuth
|
|
if !utils.IsNil(containerAuth) && containerAuth.Profile != "" {
|
|
// container-based auth
|
|
containerAuthProfile := iStore.store.Auth.ContainerAuth.Profile
|
|
containerAuthToken := iStore.store.Auth.ContainerAuth.TokenLocation
|
|
containerAuthEndpoint := iStore.store.Auth.ContainerAuth.IAMEndpoint
|
|
|
|
if containerAuthToken == "" {
|
|
// API default path
|
|
containerAuthToken = "/var/run/secrets/tokens/vault-token"
|
|
}
|
|
if containerAuthEndpoint == "" {
|
|
// API default path
|
|
containerAuthEndpoint = "https://iam.cloud.ibm.com"
|
|
}
|
|
|
|
authenticator, err := core.NewContainerAuthenticatorBuilder().
|
|
SetIAMProfileName(containerAuthProfile).
|
|
SetCRTokenFilename(containerAuthToken).
|
|
SetURL(containerAuthEndpoint).
|
|
Build()
|
|
if err != nil {
|
|
return nil, fmt.Errorf(errIBMClient, err)
|
|
}
|
|
secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
|
|
URL: *storeSpec.Provider.IBM.ServiceURL,
|
|
Authenticator: authenticator,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf(errIBMClient, err)
|
|
}
|
|
} else {
|
|
// API Key-based auth
|
|
if err := iStore.setAuth(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
|
|
URL: *storeSpec.Provider.IBM.ServiceURL,
|
|
Authenticator: &core.IamAuthenticator{
|
|
ApiKey: string(iStore.credentials),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Setup retry options, but only if present
|
|
if storeSpec.RetrySettings != nil {
|
|
var retryAmount int
|
|
var retryDuration time.Duration
|
|
|
|
if storeSpec.RetrySettings.MaxRetries != nil {
|
|
retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
|
|
} else {
|
|
retryAmount = 3
|
|
}
|
|
|
|
if storeSpec.RetrySettings.RetryInterval != nil {
|
|
retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
|
|
} else {
|
|
retryDuration = 5 * time.Second
|
|
}
|
|
|
|
if err == nil {
|
|
secretsManager.Service.EnableRetries(retryAmount, retryDuration)
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf(errIBMClient, err)
|
|
}
|
|
|
|
ibm.IBMClient = secretsManager
|
|
return ibm, nil
|
|
}
|
|
|
|
func init() {
|
|
esv1beta1.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
|
|
IBM: &esv1beta1.IBMProvider{},
|
|
})
|
|
}
|
|
|
|
// populateSecretMap populates the secretMap with metadata information that is pulled from IBM provider.
|
|
func populateSecretMap(secretMap map[string][]byte, secretDataMap map[string]any) map[string][]byte {
|
|
for key, value := range secretDataMap {
|
|
secretMap[key] = []byte(fmt.Sprintf("%v", value))
|
|
}
|
|
return secretMap
|
|
}
|
|
|
|
func formSecretMap(secretData any) (map[string]any, error) {
|
|
secretDataMap := make(map[string]any)
|
|
data, err := json.Marshal(secretData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(errJSONSecretMarshal, err)
|
|
}
|
|
if err := json.Unmarshal(data, &secretDataMap); err != nil {
|
|
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
|
|
}
|
|
return secretDataMap, nil
|
|
}
|