2022-06-13 19:49:05 +00:00
|
|
|
/*
|
|
|
|
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 kubernetes
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
authenticationv1 "k8s.io/api/authentication/v1"
|
2022-06-13 19:49:05 +00:00
|
|
|
corev1 "k8s.io/api/core/v1"
|
2022-08-19 15:32:06 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2022-06-13 19:49:05 +00:00
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
|
|
|
|
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
|
|
|
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
errInvalidClusterStoreMissingNamespace = "missing namespace"
|
|
|
|
errFetchCredentials = "could not fetch credentials: %w"
|
|
|
|
errMissingCredentials = "missing credentials: \"%s\""
|
|
|
|
errEmptyKey = "key %s found but empty"
|
2022-08-19 15:32:06 +00:00
|
|
|
errUnableCreateToken = "cannot create service account token: %q"
|
2022-06-13 19:49:05 +00:00
|
|
|
)
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) setAuth(ctx context.Context) error {
|
|
|
|
err := c.setCA(ctx)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.store.Auth.Token != nil {
|
|
|
|
c.BearerToken, err = c.fetchSecretKey(ctx, c.store.Auth.Token.BearerToken)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.store.Auth.ServiceAccount != nil {
|
|
|
|
c.BearerToken, err = c.serviceAccountToken(ctx, c.store.Auth.ServiceAccount)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.store.Auth.Cert != nil {
|
|
|
|
return c.setClientCert(ctx)
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
|
|
|
return fmt.Errorf("no credentials provided")
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) setCA(ctx context.Context) error {
|
|
|
|
if c.store.Server.CABundle != nil {
|
|
|
|
c.CA = c.store.Server.CABundle
|
2022-06-13 19:49:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.store.Server.CAProvider != nil {
|
2022-06-13 19:49:05 +00:00
|
|
|
var ca []byte
|
|
|
|
var err error
|
2022-08-19 15:32:06 +00:00
|
|
|
switch c.store.Server.CAProvider.Type {
|
2022-06-13 19:49:05 +00:00
|
|
|
case esv1beta1.CAProviderTypeConfigMap:
|
|
|
|
keySelector := esmeta.SecretKeySelector{
|
2022-08-19 15:32:06 +00:00
|
|
|
Name: c.store.Server.CAProvider.Name,
|
|
|
|
Namespace: c.store.Server.CAProvider.Namespace,
|
|
|
|
Key: c.store.Server.CAProvider.Key,
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
ca, err = c.fetchConfigMapKey(ctx, keySelector)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
|
|
|
|
}
|
|
|
|
case esv1beta1.CAProviderTypeSecret:
|
|
|
|
keySelector := esmeta.SecretKeySelector{
|
2022-08-19 15:32:06 +00:00
|
|
|
Name: c.store.Server.CAProvider.Name,
|
|
|
|
Namespace: c.store.Server.CAProvider.Namespace,
|
|
|
|
Key: c.store.Server.CAProvider.Key,
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
ca, err = c.fetchSecretKey(ctx, keySelector)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
|
|
|
|
}
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
c.CA = ca
|
2022-06-13 19:49:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fmt.Errorf("no Certificate Authority provided")
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) setClientCert(ctx context.Context) error {
|
2022-06-13 19:49:05 +00:00
|
|
|
var err error
|
2022-08-19 15:32:06 +00:00
|
|
|
c.Certificate, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to fetch client certificate: %w", err)
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
c.Key, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientKey)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to fetch client key: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) serviceAccountToken(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) ([]byte, error) {
|
|
|
|
namespace := c.namespace
|
|
|
|
if (c.storeKind == esv1beta1.ClusterSecretStoreKind) &&
|
2022-06-13 19:49:05 +00:00
|
|
|
(serviceAccountRef.Namespace != nil) {
|
2022-08-19 15:32:06 +00:00
|
|
|
namespace = *serviceAccountRef.Namespace
|
|
|
|
}
|
|
|
|
expirationSeconds := int64(3600)
|
|
|
|
tr, err := c.ctrlClientset.ServiceAccounts(namespace).CreateToken(ctx, serviceAccountRef.Name, &authenticationv1.TokenRequest{
|
|
|
|
Spec: authenticationv1.TokenRequestSpec{
|
|
|
|
Audiences: serviceAccountRef.Audiences,
|
|
|
|
ExpirationSeconds: &expirationSeconds,
|
|
|
|
},
|
|
|
|
}, metav1.CreateOptions{})
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
2022-08-19 15:32:06 +00:00
|
|
|
return nil, fmt.Errorf(errUnableCreateToken, err)
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
return []byte(tr.Status.Token), nil
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
|
2022-06-13 19:49:05 +00:00
|
|
|
keySecret := &corev1.Secret{}
|
|
|
|
objectKey := types.NamespacedName{
|
|
|
|
Name: key.Name,
|
2022-08-19 15:32:06 +00:00
|
|
|
Namespace: c.namespace,
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
|
|
|
// only ClusterStore is allowed to set namespace (and then it's required)
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.storeKind == esv1beta1.ClusterSecretStoreKind {
|
2022-06-13 19:49:05 +00:00
|
|
|
if key.Namespace == nil {
|
|
|
|
return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
|
|
|
|
}
|
|
|
|
objectKey.Namespace = *key.Namespace
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
err := c.ctrlClient.Get(ctx, objectKey, keySecret)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(errFetchCredentials, err)
|
|
|
|
}
|
|
|
|
val, ok := keySecret.Data[key.Key]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf(errMissingCredentials, key.Key)
|
|
|
|
}
|
|
|
|
if len(val) == 0 {
|
|
|
|
return nil, fmt.Errorf(errEmptyKey, key.Key)
|
|
|
|
}
|
|
|
|
return val, nil
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:32:06 +00:00
|
|
|
func (c *Client) fetchConfigMapKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
|
2022-06-13 19:49:05 +00:00
|
|
|
configMap := &corev1.ConfigMap{}
|
|
|
|
objectKey := types.NamespacedName{
|
|
|
|
Name: key.Name,
|
2022-08-19 15:32:06 +00:00
|
|
|
Namespace: c.namespace,
|
2022-06-13 19:49:05 +00:00
|
|
|
}
|
|
|
|
// only ClusterStore is allowed to set namespace (and then it's required)
|
2022-08-19 15:32:06 +00:00
|
|
|
if c.storeKind == esv1beta1.ClusterSecretStoreKind {
|
2022-06-13 19:49:05 +00:00
|
|
|
if key.Namespace == nil {
|
|
|
|
return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
|
|
|
|
}
|
|
|
|
objectKey.Namespace = *key.Namespace
|
|
|
|
}
|
2022-08-19 15:32:06 +00:00
|
|
|
err := c.ctrlClient.Get(ctx, objectKey, configMap)
|
2022-06-13 19:49:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf(errFetchCredentials, err)
|
|
|
|
}
|
|
|
|
val, ok := configMap.Data[key.Key]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf(errMissingCredentials, key.Key)
|
|
|
|
}
|
|
|
|
if val == "" {
|
|
|
|
return nil, fmt.Errorf(errEmptyKey, key.Key)
|
|
|
|
}
|
|
|
|
return []byte(val), nil
|
|
|
|
}
|