|
@@ -26,12 +26,14 @@ import (
|
|
|
"path"
|
|
"path"
|
|
|
"regexp"
|
|
"regexp"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
|
|
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
|
|
|
"github.com/Azure/go-autorest/autorest"
|
|
"github.com/Azure/go-autorest/autorest"
|
|
|
"github.com/Azure/go-autorest/autorest/adal"
|
|
"github.com/Azure/go-autorest/autorest/adal"
|
|
|
"github.com/Azure/go-autorest/autorest/azure"
|
|
"github.com/Azure/go-autorest/autorest/azure"
|
|
|
kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
|
|
kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
|
|
|
|
|
+ "github.com/Azure/go-autorest/autorest/date"
|
|
|
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
|
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
|
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
|
|
"github.com/tidwall/gjson"
|
|
"github.com/tidwall/gjson"
|
|
@@ -52,6 +54,7 @@ import (
|
|
|
"github.com/external-secrets/external-secrets/pkg/constants"
|
|
"github.com/external-secrets/external-secrets/pkg/constants"
|
|
|
"github.com/external-secrets/external-secrets/pkg/metrics"
|
|
"github.com/external-secrets/external-secrets/pkg/metrics"
|
|
|
"github.com/external-secrets/external-secrets/pkg/utils"
|
|
"github.com/external-secrets/external-secrets/pkg/utils"
|
|
|
|
|
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
|
|
|
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
|
|
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
@@ -119,6 +122,10 @@ type Azure struct {
|
|
|
namespace string
|
|
namespace string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+type PushSecretMetadataSpec struct {
|
|
|
|
|
+ ExpirationDate string `json:"expirationDate,omitempty"`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func init() {
|
|
func init() {
|
|
|
esv1beta1.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
|
|
esv1beta1.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
|
|
|
AzureKV: &esv1beta1.AzureKVProvider{},
|
|
AzureKV: &esv1beta1.AzureKVProvider{},
|
|
@@ -411,7 +418,7 @@ func canCreate(tags map[string]*string, err error) (bool, error) {
|
|
|
return true, nil
|
|
return true, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value []byte) error {
|
|
|
|
|
|
|
+func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value []byte, expires *date.UnixTime) error {
|
|
|
secret, err := a.baseClient.GetSecret(ctx, *a.provider.VaultURL, secretName, "")
|
|
secret, err := a.baseClient.GetSecret(ctx, *a.provider.VaultURL, secretName, "")
|
|
|
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
|
|
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
|
|
|
ok, err := canCreate(secret.Tags, err)
|
|
ok, err := canCreate(secret.Tags, err)
|
|
@@ -423,8 +430,14 @@ func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value
|
|
|
}
|
|
}
|
|
|
val := string(value)
|
|
val := string(value)
|
|
|
if secret.Value != nil && val == *secret.Value {
|
|
if secret.Value != nil && val == *secret.Value {
|
|
|
- return nil
|
|
|
|
|
|
|
+ if secret.Attributes != nil {
|
|
|
|
|
+ if (secret.Attributes.Expires == nil && expires == nil) ||
|
|
|
|
|
+ (secret.Attributes.Expires != nil && expires != nil && *secret.Attributes.Expires == *expires) {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
secretParams := keyvault.SecretSetParameters{
|
|
secretParams := keyvault.SecretSetParameters{
|
|
|
Value: &val,
|
|
Value: &val,
|
|
|
Tags: map[string]*string{
|
|
Tags: map[string]*string{
|
|
@@ -434,6 +447,11 @@ func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value
|
|
|
Enabled: pointer.To(true),
|
|
Enabled: pointer.To(true),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if expires != nil {
|
|
|
|
|
+ secretParams.SecretAttributes.Expires = expires
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
_, err = a.baseClient.SetSecret(ctx, *a.provider.VaultURL, secretName, secretParams)
|
|
_, err = a.baseClient.SetSecret(ctx, *a.provider.VaultURL, secretName, secretParams)
|
|
|
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
|
|
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -534,8 +552,9 @@ func (a *Azure) setKeyVaultKey(ctx context.Context, secretName string, value []b
|
|
|
// PushSecret stores secrets into a Key vault instance.
|
|
// PushSecret stores secrets into a Key vault instance.
|
|
|
func (a *Azure) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
|
|
func (a *Azure) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
|
|
|
var (
|
|
var (
|
|
|
- value []byte
|
|
|
|
|
- err error
|
|
|
|
|
|
|
+ value []byte
|
|
|
|
|
+ err error
|
|
|
|
|
+ expires *date.UnixTime
|
|
|
)
|
|
)
|
|
|
if data.GetSecretKey() == "" {
|
|
if data.GetSecretKey() == "" {
|
|
|
// Must convert secret values to string, otherwise data will be sent as base64 to Vault
|
|
// Must convert secret values to string, otherwise data will be sent as base64 to Vault
|
|
@@ -551,10 +570,24 @@ func (a *Azure) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1
|
|
|
value = secret.Data[data.GetSecretKey()]
|
|
value = secret.Data[data.GetSecretKey()]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ metadata, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](data.GetMetadata())
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to parse push secret metadata: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if metadata != nil && metadata.Spec.ExpirationDate != "" {
|
|
|
|
|
+ t, err := time.Parse(time.RFC3339, metadata.Spec.ExpirationDate)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("error parsing expiration date in metadata: %w. Expected format: YYYY-MM-DDTHH:MM:SSZ (RFC3339). Example: 2024-12-31T20:00:00Z", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ unixTime := date.UnixTime(t)
|
|
|
|
|
+ expires = &unixTime
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
objectType, secretName := getObjType(esv1beta1.ExternalSecretDataRemoteRef{Key: data.GetRemoteKey()})
|
|
objectType, secretName := getObjType(esv1beta1.ExternalSecretDataRemoteRef{Key: data.GetRemoteKey()})
|
|
|
switch objectType {
|
|
switch objectType {
|
|
|
case defaultObjType:
|
|
case defaultObjType:
|
|
|
- return a.setKeyVaultSecret(ctx, secretName, value)
|
|
|
|
|
|
|
+ return a.setKeyVaultSecret(ctx, secretName, value, expires)
|
|
|
case objectTypeCert:
|
|
case objectTypeCert:
|
|
|
return a.setKeyVaultCertificate(ctx, secretName, value)
|
|
return a.setKeyVaultCertificate(ctx, secretName, value)
|
|
|
case objectTypeKey:
|
|
case objectTypeKey:
|