feat(k8s): upgrade to kubebuilder v4

This commit is contained in:
Daniel Hougaard
2025-08-08 05:07:43 +04:00
parent 6100086338
commit 847c50d2d4
25 changed files with 459 additions and 534 deletions

View File

@@ -42,9 +42,11 @@ import (
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
type InfisicalDynamicSecretReconciler struct {
client.Client
BaseLogger logr.Logger
Scheme *runtime.Scheme
Random *rand.Rand
BaseLogger logr.Logger
Scheme *runtime.Scheme
Random *rand.Rand
Namespace string
IsNamespaceScoped bool
}
var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
@@ -106,7 +108,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
}
// Initialize the business logic handler
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme)
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
err := handler.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap)
@@ -126,7 +128,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
}
// Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client)
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{
@@ -135,7 +137,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
}
// Initialize the business logic handler
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme)
handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
// Setup API configuration through business logic
err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig)
@@ -169,7 +171,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
}, nil
}
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference)
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference, r.IsNamespaceScoped)
handler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err)
if err != nil {

View File

@@ -45,9 +45,10 @@ import (
// InfisicalPushSecretReconciler reconciles a InfisicalPushSecretSecret object
type InfisicalPushSecretReconciler struct {
client.Client
IsNamespaceScoped bool
BaseLogger logr.Logger
Scheme *runtime.Scheme
IsNamespaceScoped bool
Namespace string
}
var infisicalPushSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
@@ -158,7 +159,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
}
// Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client)
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil {
if requeueTime != 0 {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))

View File

@@ -41,8 +41,10 @@ import (
// InfisicalSecretReconciler reconciles a InfisicalSecret object
type InfisicalSecretReconciler struct {
client.Client
BaseLogger logr.Logger
Scheme *runtime.Scheme
BaseLogger logr.Logger
Scheme *runtime.Scheme
Namespace string
IsNamespaceScoped bool
}
var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
@@ -51,9 +53,16 @@ func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName)
}
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete
//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete
//+kubebuilder:rbac:groups=apps,resources=deployments;daemonsets;statefulsets,verbs=list;watch;get;update
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch
//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list
//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create
//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
@@ -138,7 +147,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
// Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client)
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{
@@ -147,7 +156,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}
// Initialize the business logic handler
handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme)
handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped)
// Setup API configuration through business logic
err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig)
@@ -177,7 +186,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}, nil
}
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences)
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences, r.IsNamespaceScoped)
handler.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
if err != nil {

View File

@@ -7,6 +7,7 @@ import (
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/internal/constants"
"github.com/Infisical/infisical/k8-operator/internal/util"
"github.com/go-logr/logr"
v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -19,7 +20,7 @@ import (
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) {
func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) {
listOfDeployments := &v1.DeploymentList{}
err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
@@ -47,6 +48,10 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll
managedKubeSecret := &corev1.Secret{}
err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
if err != nil {
if util.IsNamespaceScopedError(err, isNamespaceScoped) {
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the secret is in the same namespace as the operator. [err=%v]", err)
}
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
}
@@ -100,9 +105,9 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll
return 0, nil
}
func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig) (int, error) {
func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) {
for _, managedSecret := range managedSecrets {
_, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret)
_, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret, isNamespaceScoped)
if err != nil {
logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName))
return 0, err
@@ -259,11 +264,17 @@ func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, l
return nil
}
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
func GetInfisicalConfigMap(ctx context.Context, client client.Client, isNamespaceScoped bool) (configMap map[string]string, errToReturn error) {
// default key values
defaultConfigMapData := make(map[string]string)
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
// this will never work if we're namespace scoped, because the operator can't read outside of its namespace by our current RBAC rules.
// This is how it has always worked, but the error has been masked as 'not found' in V3 kubebuilder.
if isNamespaceScoped {
return defaultConfigMapData, nil
}
kubeConfigMap := &corev1.ConfigMap{}
err := client.Get(ctx, types.NamespacedName{
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,

View File

@@ -1,45 +0,0 @@
package controllerhelpers
import (
"context"
"fmt"
"github.com/Infisical/infisical/k8-operator/internal/constants"
corev1 "k8s.io/api/core/v1"
k8Errors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
// default key values
defaultConfigMapData := make(map[string]string)
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
kubeConfigMap := &corev1.ConfigMap{}
err := client.Get(ctx, types.NamespacedName{
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
}, kubeConfigMap)
if err != nil {
if k8Errors.IsNotFound(err) {
kubeConfigMap = nil
} else {
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
}
}
if kubeConfigMap == nil {
return defaultConfigMapData, nil
} else {
for key, value := range defaultConfigMapData {
_, exists := kubeConfigMap.Data[key]
if !exists {
kubeConfigMap.Data[key] = value
}
}
return kubeConfigMap.Data, nil
}
}

View File

@@ -19,15 +19,17 @@ import (
type InfisicalDynamicSecretHandler struct {
client.Client
Scheme *runtime.Scheme
Random *rand.Rand
Scheme *runtime.Scheme
Random *rand.Rand
IsNamespaceScoped bool
}
func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalDynamicSecretHandler {
func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalDynamicSecretHandler {
return &InfisicalDynamicSecretHandler{
Client: client,
Scheme: scheme,
Random: rand.New(rand.NewSource(time.Now().UnixNano())),
Client: client,
Scheme: scheme,
Random: rand.New(rand.NewSource(time.Now().UnixNano())),
IsNamespaceScoped: isNamespaceScoped,
}
}
@@ -51,6 +53,10 @@ func (h *InfisicalDynamicSecretHandler) getInfisicalCaCertificateFromKubeSecret(
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
}
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
}
if err != nil {
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
}
@@ -75,36 +81,40 @@ func (h *InfisicalDynamicSecretHandler) HandleCACertificate(ctx context.Context,
func (h *InfisicalDynamicSecretHandler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) {
reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
}
return reconciler.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecret, resourceVariablesMap)
}
func (h *InfisicalDynamicSecretHandler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
}
return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap)
}
func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
}
reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn)
}
func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) {
reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
Client: h.Client,
Scheme: h.Scheme,
Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
}
reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn)
}

View File

@@ -2,7 +2,6 @@ package infisicaldynamicsecret
import (
"context"
"errors"
"fmt"
"math/rand"
"strings"
@@ -27,8 +26,9 @@ import (
type InfisicalDynamicSecretReconciler struct {
client.Client
Scheme *runtime.Scheme
Random *rand.Rand
Scheme *runtime.Scheme
Random *rand.Rand
IsNamespaceScoped bool
}
func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error {
@@ -85,36 +85,6 @@ func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx
return nil
}
func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) {
authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){
util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth,
util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth,
util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth,
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {
authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{
Secret: infisicalSecret,
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
}, infisicalClient)
if err == nil {
return authDetails, nil
}
if !errors.Is(err, util.ErrAuthNotApplicable) {
return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
}
}
return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
}
func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
var resourceVariables util.ResourceVariables
@@ -254,7 +224,10 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con
infisicalClient := resourceVariables.InfisicalClient
logger.Info("Authenticating for lease revocation")
authDetails, err := r.handleAuthentication(ctx, *infisicalDynamicSecret, infisicalClient)
authDetails, err := util.HandleAuthentication(ctx, util.SecretAuthInput{
Secret: *infisicalDynamicSecret,
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
}, r.Client, infisicalClient, r.IsNamespaceScoped)
if err != nil {
return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err)
@@ -290,6 +263,9 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con
})
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
}
return fmt.Errorf("unable to fetch destination secret [err=%s]", err)
}
@@ -318,7 +294,10 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate")
authDetails, err = r.handleAuthentication(ctx, *infisicalDynamicSecret, infisicalClient)
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
Secret: *infisicalDynamicSecret,
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
}, r.Client, infisicalClient, r.IsNamespaceScoped)
if err != nil {
return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err)
@@ -337,6 +316,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
})
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
}
if k8Errors.IsNotFound(err) {
annotationValue := ""
@@ -352,6 +334,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
})
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret after creation. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
}
return nextReconcile, fmt.Errorf("unable to fetch destination secret after creation [err=%s]", err)
}

View File

@@ -49,6 +49,10 @@ func (h *InfisicalPushSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
}
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
}
if err != nil {
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
}

View File

@@ -3,7 +3,6 @@ package infisicalpushsecret
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
tpl "text/template"
@@ -30,36 +29,6 @@ type InfisicalPushSecretReconciler struct {
IsNamespaceScoped bool
}
func (r *InfisicalPushSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalPushSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) {
authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){
util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth,
util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth,
util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth,
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {
authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{
Secret: infisicalSecret,
Type: util.SecretCrd.INFISICAL_PUSH_SECRET,
}, infisicalClient)
if err == nil {
return authDetails, nil
}
if !errors.Is(err, util.ErrAuthNotApplicable) {
return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
}
}
return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
}
func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
var resourceVariables util.ResourceVariables
@@ -192,7 +161,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate")
authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient)
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
Secret: *infisicalPushSecret,
Type: util.SecretCrd.INFISICAL_PUSH_SECRET,
}, r.Client, infisicalClient, r.IsNamespaceScoped)
r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
if err != nil {
@@ -215,6 +187,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
})
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err)
}
return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
}
@@ -539,7 +515,10 @@ func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context
if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate")
authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient)
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
Secret: *infisicalPushSecret,
Type: util.SecretCrd.INFISICAL_PUSH_SECRET,
}, r.Client, infisicalClient, r.IsNamespaceScoped)
r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
if err != nil {

View File

@@ -17,13 +17,15 @@ import (
type InfisicalSecretHandler struct {
client.Client
Scheme *runtime.Scheme
Scheme *runtime.Scheme
IsNamespaceScoped bool
}
func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalSecretHandler {
func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalSecretHandler {
return &InfisicalSecretHandler{
Client: client,
Scheme: scheme,
Client: client,
Scheme: scheme,
IsNamespaceScoped: isNamespaceScoped,
}
}
@@ -48,6 +50,9 @@ func (h *InfisicalSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx con
}
if err != nil {
if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) {
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
}
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
}
@@ -71,24 +76,27 @@ func (h *InfisicalSecretHandler) HandleCACertificate(ctx context.Context, infisi
func (h *InfisicalSecretHandler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig, resourceVariablesMap map[string]util.ResourceVariables) (int, error) {
reconciler := &InfisicalSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Client: h.Client,
Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
}
return reconciler.ReconcileInfisicalSecret(ctx, logger, infisicalSecret, managedKubeSecretReferences, managedKubeConfigMapReferences, resourceVariablesMap)
}
func (h *InfisicalSecretHandler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) {
reconciler := &InfisicalSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Client: h.Client,
Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
}
reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn)
}
func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) {
reconciler := &InfisicalSecretReconciler{
Client: h.Client,
Scheme: h.Scheme,
Client: h.Client,
Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
}
reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn)
}

View File

@@ -32,59 +32,8 @@ const FINALIZER_NAME = "secrets.finalizers.infisical.com"
type InfisicalSecretReconciler struct {
client.Client
Scheme *runtime.Scheme
}
func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) {
// ? Legacy support, service token auth
infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err)
}
if infisicalToken != "" {
infisicalClient.Auth().SetAccessToken(infisicalToken)
return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_TOKEN}, nil
}
// ? Legacy support, service account auth
serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret)
if err != nil {
return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err)
}
if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" {
infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey)
return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_ACCOUNT}, nil
}
authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){
util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth,
util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth,
util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth,
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {
authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{
Secret: infisicalSecret,
Type: util.SecretCrd.INFISICAL_SECRET,
}, infisicalClient)
if err == nil {
return authDetails, nil
}
if !errors.Is(err, util.ErrAuthNotApplicable) {
return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
}
}
return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
Scheme *runtime.Scheme
IsNamespaceScoped bool
}
func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) {
@@ -105,11 +54,14 @@ func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.
Name: secretName,
})
if k8Errors.IsNotFound(err) {
if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return "", nil
}
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err)
}
return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err)
}
@@ -118,39 +70,26 @@ func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.
return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil
}
func (r *InfisicalSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) {
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace,
Name: infisicalSecret.Spec.TLS.CaRef.SecretName,
})
if k8Errors.IsNotFound(err) {
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
}
if err != nil {
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
}
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey])
return caCertificateFromSecret, nil
}
// Fetches service account credentials from a Kubernetes secret specified in the infisicalSecret object, extracts the access key, public key, and private key from the secret, and returns them as a ServiceAccountCredentials object.
// If any keys are missing or an error occurs, returns an empty object or an error object, respectively.
func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) {
secretNamespace := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace
secretName := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName
serviceAccountCredsFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
Namespace: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace,
Name: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName,
Namespace: secretNamespace,
Name: secretName,
})
if k8Errors.IsNotFound(err) {
if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return model.ServiceAccountDetails{}, nil
}
if err != nil {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return model.ServiceAccountDetails{}, fmt.Errorf("unable to fetch Kubernetes service account credentials secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the service account credentials secret is in the same namespace as the operator. [err=%v]", err)
}
return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err)
}
@@ -503,7 +442,11 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate")
authDetails, err = r.handleAuthentication(ctx, *infisicalSecret, infisicalClient)
authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{
Secret: *infisicalSecret,
Type: util.SecretCrd.INFISICAL_SECRET,
}, r.Client, infisicalClient, r.IsNamespaceScoped)
r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err)
if err != nil {
@@ -533,6 +476,9 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
})
if err != nil && !k8Errors.IsNotFound(err) {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return 0, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the secret is in the same namespace as the operator. [err=%v]", err)
}
return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err)
}
@@ -557,6 +503,9 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
})
if err != nil && !k8Errors.IsNotFound(err) {
if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) {
return 0, fmt.Errorf("unable to fetch Kubernetes config map. Your Operator installation is namespace scoped, and cannot read config maps outside of the namespace it is installed in. Please ensure the config map is in the same namespace as the operator. [err=%v]", err)
}
return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err)
}

View File

@@ -16,7 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string) (string, error) {
func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string, isNamespaceScoped bool) (string, error) {
if autoCreateServiceAccountToken {
restClient, err := GetRestClientFromClient()
@@ -57,6 +57,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc
serviceAccount := &corev1.ServiceAccount{}
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount)
if err != nil {
if IsNamespaceScopedError(err, isNamespaceScoped) {
return "", fmt.Errorf("unable to fetch service account. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the service account is in the same namespace as the operator. [err=%v]", err)
}
return "", err
}
@@ -69,6 +72,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc
secret := &corev1.Secret{}
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret)
if err != nil {
if IsNamespaceScopedError(err, isNamespaceScoped) {
return "", fmt.Errorf("unable to fetch service account token secret. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the service account token secret is in the same namespace as the operator. [err=%v]", err)
}
return "", err
}
@@ -127,7 +133,7 @@ type AuthenticationDetails struct {
var ErrAuthNotApplicable = errors.New("authentication not applicable")
func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) {
var universalAuthSpec v1alpha1.UniversalAuthDetails
@@ -164,10 +170,14 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se
}
}
if universalAuthSpec.CredentialsRef.SecretName == "" || universalAuthSpec.CredentialsRef.SecretNamespace == "" {
return AuthenticationDetails{}, ErrAuthNotApplicable
}
universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace,
SecretName: universalAuthSpec.CredentialsRef.SecretName,
})
}, isNamespaceScoped)
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err)
@@ -190,7 +200,7 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se
}, nil
}
func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) {
var ldapAuthSpec v1alpha1.LdapAuthDetails
@@ -229,10 +239,14 @@ func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretC
}
}
if ldapAuthSpec.CredentialsRef.SecretName == "" || ldapAuthSpec.CredentialsRef.SecretNamespace == "" {
return AuthenticationDetails{}, ErrAuthNotApplicable
}
ldapAuthKubeSecret, err := GetInfisicalLdapAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace,
SecretName: ldapAuthSpec.CredentialsRef.SecretName,
})
}, isNamespaceScoped)
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err)
@@ -255,7 +269,7 @@ func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretC
}, nil
}
func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) {
var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails
switch secretCrd.Type {
@@ -312,6 +326,7 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s
kubernetesAuthSpec.ServiceAccountRef.Name,
kubernetesAuthSpec.AutoCreateServiceAccountToken,
kubernetesAuthSpec.ServiceAccountTokenAudiences,
isNamespaceScoped,
)
if err != nil {
@@ -332,7 +347,7 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s
}
func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) {
awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{}
switch secretCrd.Type {
@@ -387,7 +402,7 @@ func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secre
}
func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) {
azureAuthSpec := v1alpha1.AzureAuthDetails{}
switch secretCrd.Type {
@@ -445,7 +460,7 @@ func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secret
}
func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) {
gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{}
switch secretCrd.Type {
@@ -500,7 +515,7 @@ func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, s
}
func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) {
func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) {
gcpIamSpec := v1alpha1.GcpIamAuthDetails{}
switch secretCrd.Type {
@@ -555,3 +570,61 @@ func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secre
SecretType: secretCrd.Type,
}, nil
}
func HandleAuthentication(ctx context.Context, secretInput SecretAuthInput, reconcilerClient client.Client, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) {
// We only support legacy auth for InfisicalSecret CRD
if secretInput.Type == SecretCrd.INFISICAL_SECRET {
infisicalSecret, ok := secretInput.Secret.(v1alpha1.InfisicalSecret)
if !ok {
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret")
}
// ? Legacy support, service token auth
infisicalToken, err := GetInfisicalTokenFromKubeSecret(ctx, reconcilerClient, infisicalSecret)
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err)
}
if infisicalToken != "" {
infisicalClient.Auth().SetAccessToken(infisicalToken)
return AuthenticationDetails{AuthStrategy: AuthStrategy.SERVICE_TOKEN}, nil
}
// ? Legacy support, service account auth
serviceAccountCreds, err := GetInfisicalServiceAccountCredentialsFromKubeSecret(ctx, reconcilerClient, infisicalSecret)
if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err)
}
if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" {
infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey)
return AuthenticationDetails{AuthStrategy: AuthStrategy.SERVICE_ACCOUNT}, nil
}
}
authStrategies := map[AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error){
AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: HandleUniversalAuth,
AuthStrategy.KUBERNETES_MACHINE_IDENTITY: HandleKubernetesAuth,
AuthStrategy.AWS_IAM_MACHINE_IDENTITY: HandleAwsIamAuth,
AuthStrategy.AZURE_MACHINE_IDENTITY: HandleAzureAuth,
AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: HandleGcpIdTokenAuth,
AuthStrategy.GCP_IAM_MACHINE_IDENTITY: HandleGcpIamAuth,
AuthStrategy.LDAP_MACHINE_IDENTITY: HandleLdapAuth,
}
for authStrategy, authHandler := range authStrategies {
authDetails, err := authHandler(ctx, reconcilerClient, secretInput, infisicalClient, isNamespaceScoped)
if err == nil {
return authDetails, nil
}
if !errors.Is(err, ErrAuthNotApplicable) {
return AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
}
}
return AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
}

View File

@@ -54,3 +54,7 @@ func AppendAPIEndpoint(address string) string {
}
return address + "/api"
}
func IsNamespaceScopedError(err error, isNamespaceScoped bool) bool {
return isNamespaceScoped && err != nil && strings.Contains(err.Error(), "unknown namespace for the cache")
}

View File

@@ -3,8 +3,10 @@ package util
import (
"context"
"fmt"
"strings"
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/internal/constants"
"github.com/Infisical/infisical/k8-operator/internal/model"
corev1 "k8s.io/api/core/v1"
k8Errors "k8s.io/apimachinery/pkg/api/errors"
@@ -41,13 +43,11 @@ func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient clie
return kubeConfigMap, err
}
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) {
func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference, isNamespaceScoped bool) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) {
universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: universalAuthRef.SecretNamespace,
Name: universalAuthRef.SecretName,
// Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace,
// Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName,
})
if k8Errors.IsNotFound(err) {
@@ -55,6 +55,9 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie
}
if err != nil {
if IsNamespaceScopedError(err, isNamespaceScoped) {
return model.UniversalAuthIdentityDetails{}, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the secret is in the same namespace as the operator. [err=%v]", err)
}
return model.UniversalAuthIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
}
@@ -65,7 +68,7 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie
}
func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.LdapIdentityDetails, err error) {
func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference, isNamespaceScoped bool) (machineIdentityDetails model.LdapIdentityDetails, err error) {
ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: ldapAuthRef.SecretNamespace,
@@ -77,6 +80,9 @@ func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient cl
}
if err != nil {
if IsNamespaceScopedError(err, isNamespaceScoped) {
return model.LdapIdentityDetails{}, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the secret is in the same namespace as the operator. [err=%v]", err)
}
return model.LdapIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err)
}
@@ -115,3 +121,63 @@ func GetRestClientFromClient() (rest.Interface, error) {
return clientset.CoreV1().RESTClient(), nil
}
func GetInfisicalTokenFromKubeSecret(ctx context.Context, reconcilerClient client.Client, infisicalSecret v1alpha1.InfisicalSecret) (string, error) {
// default to new secret ref structure
secretName := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretName
secretNamespace := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretNamespace
// fall back to previous secret ref
if secretName == "" {
secretName = infisicalSecret.Spec.TokenSecretReference.SecretName
}
if secretNamespace == "" {
secretNamespace = infisicalSecret.Spec.TokenSecretReference.SecretNamespace
}
tokenSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: secretNamespace,
Name: secretName,
})
if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return "", nil
}
if err != nil {
return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err)
}
infisicalServiceToken := tokenSecret.Data[constants.INFISICAL_TOKEN_SECRET_KEY_NAME]
return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil
}
func GetInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, reconcilerClient client.Client, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) {
secretNamespace := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace
secretName := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName
serviceAccountCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: secretNamespace,
Name: secretName,
})
if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return model.ServiceAccountDetails{}, nil
}
if err != nil {
return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err)
}
accessKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_ACCESS_KEY]
publicKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PUBLIC_KEY]
privateKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PRIVATE_KEY]
if accessKeyFromSecret == nil || publicKeyFromSecret == nil || privateKeyFromSecret == nil {
return model.ServiceAccountDetails{}, nil
}
return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil
}