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

@@ -37,7 +37,7 @@ legacy-helm: manifests kustomize helmify
helm: manifests kustomize helmify
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
./scripts/generate-helm.sh
./scripts/generate-helm.sh ${VERSION}
cd config/manager && $(KUSTOMIZE) edit set image controller=controller:latest # reset back
## Yaml for Kubectl
@@ -88,7 +88,7 @@ test: manifests generate fmt vet setup-envtest ## Run tests.
# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
# CertManager is installed by default; skip with:
# - CERT_MANAGER_INSTALL_SKIP=true
KIND_CLUSTER ?= k8-operator-test-e2e
KIND_CLUSTER ?= infisical-operator-test-e2e
.PHONY: setup-test-e2e
setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
@@ -157,10 +157,10 @@ PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
docker-buildx: ## Build and push docker image for the manager for cross-platform support
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
- $(CONTAINER_TOOL) buildx create --name k8-operator-builder
$(CONTAINER_TOOL) buildx use k8-operator-builder
- $(CONTAINER_TOOL) buildx create --name infisical-operator-builder
$(CONTAINER_TOOL) buildx use infisical-operator-builder
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
- $(CONTAINER_TOOL) buildx rm k8-operator-builder
- $(CONTAINER_TOOL) buildx rm infisical-operator-builder
rm Dockerfile.cross
.PHONY: build-installer

View File

@@ -19,6 +19,7 @@ package main
import (
"crypto/tls"
"flag"
"fmt"
"os"
"path/filepath"
@@ -30,6 +31,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -63,7 +65,10 @@ func main() {
var probeAddr string
var secureMetrics bool
var enableHTTP2 bool
var namespace string
var tlsOpts []func(*tls.Config)
flag.StringVar(&namespace, "namespace", "", "Watch InfisicalSecrets scoped in the provided namespace only")
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -178,7 +183,7 @@ func main() {
})
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
managerOptions := ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
@@ -196,32 +201,51 @@ func main() {
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
})
}
// Only set cache options if we're namespace-scoped
if namespace != "" {
managerOptions.Cache = cache.Options{
Scheme: scheme,
DefaultNamespaces: map[string]cache.Config{
namespace: {}, // whichever namespace the operator is running in
},
}
ctrl.Log.Info(fmt.Sprintf("Watching CRDs in [namespace=%s]", namespace))
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions)
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err := (&controller.InfisicalSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
Namespace: namespace,
IsNamespaceScoped: namespace != "",
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret")
os.Exit(1)
}
if err := (&controller.InfisicalPushSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
IsNamespaceScoped: namespace != "",
Namespace: namespace,
BaseLogger: ctrl.Log,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret")
os.Exit(1)
}
if err := (&controller.InfisicalDynamicSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log,
IsNamespaceScoped: namespace != "",
Namespace: namespace,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
os.Exit(1)

View File

@@ -1,12 +1,12 @@
# Adds namespace to all resources.
namespace: k8-operator-system
namespace: infisical-operator-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: k8-operator-
namePrefix: infisical-operator-
# Labels to add to all resources and selectors.
#labels:
@@ -15,220 +15,12 @@ namePrefix: k8-operator-
# someName: someValue
resources:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
# [METRICS] Expose the controller manager metrics service.
- metrics_service.yaml
# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
# be able to communicate with the Webhook Server.
#- ../network-policy
- ../crd
- ../rbac
- ../manager
- metrics_service.yaml
# Uncomment the patches line if you enable Metrics
patches:
# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443.
# More info: https://book.kubebuilder.io/reference/metrics
- path: manager_metrics_patch.yaml
target:
kind: Deployment
# Uncomment the patches line if you enable Metrics and CertManager
# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line.
# This patch will protect the metrics with certManager self-signed certs.
#- path: cert_metrics_manager_patch.yaml
# target:
# kind: Deployment
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- path: manager_webhook_patch.yaml
# target:
# kind: Deployment
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
# Uncomment the following replacements to add the cert-manager CA injection annotations
#replacements:
# - source: # Uncomment the following block to enable certificates for metrics
# kind: Service
# version: v1
# name: controller-manager-metrics-service
# fieldPath: metadata.name
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: metrics-certs
# fieldPaths:
# - spec.dnsNames.0
# - spec.dnsNames.1
# options:
# delimiter: '.'
# index: 0
# create: true
# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
# kind: ServiceMonitor
# group: monitoring.coreos.com
# version: v1
# name: controller-manager-metrics-monitor
# fieldPaths:
# - spec.endpoints.0.tlsConfig.serverName
# options:
# delimiter: '.'
# index: 0
# create: true
# - source:
# kind: Service
# version: v1
# name: controller-manager-metrics-service
# fieldPath: metadata.namespace
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: metrics-certs
# fieldPaths:
# - spec.dnsNames.0
# - spec.dnsNames.1
# options:
# delimiter: '.'
# index: 1
# create: true
# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
# kind: ServiceMonitor
# group: monitoring.coreos.com
# version: v1
# name: controller-manager-metrics-monitor
# fieldPaths:
# - spec.endpoints.0.tlsConfig.serverName
# options:
# delimiter: '.'
# index: 1
# create: true
# - source: # Uncomment the following block if you have any webhook
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.name # Name of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 0
# create: true
# - source:
# kind: Service
# version: v1
# name: webhook-service
# fieldPath: .metadata.namespace # Namespace of the service
# targets:
# - select:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPaths:
# - .spec.dnsNames.0
# - .spec.dnsNames.1
# options:
# delimiter: '.'
# index: 1
# create: true
# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # This name should match the one in certificate.yaml
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets:
# - select:
# kind: ValidatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets:
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 0
# create: true
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets:
# - select:
# kind: MutatingWebhookConfiguration
# fieldPaths:
# - .metadata.annotations.[cert-manager.io/inject-ca-from]
# options:
# delimiter: '/'
# index: 1
# create: true
# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.namespace # Namespace of the certificate CR
# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
# +kubebuilder:scaffold:crdkustomizecainjectionns
# - source:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert
# fieldPath: .metadata.name
# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
# +kubebuilder:scaffold:crdkustomizecainjectionname
- path: manager_metrics_patch.yaml
target:
kind: Deployment

View File

@@ -40,7 +40,9 @@ rules:
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- statefulsets
verbs:
- get
- list

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
}

View File

@@ -1,12 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
PATH_TO_HELM_CHART="${SCRIPT_DIR}/../../helm-charts/secrets-operator"
PROJECT_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
HELM_DIR="${PROJECT_ROOT}/../helm-charts/secrets-operator"
LOCALBIN="${PROJECT_ROOT}/bin"
KUSTOMIZE="${LOCALBIN}/kustomize"
HELMIFY="${LOCALBIN}/helmify"
VERSION=$1
VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') # needed to validate semver
# Version validation
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
if ! [[ "$VERSION_WITHOUT_V" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must follow semantic versioning (e.g. 0.0.1)"
exit 1
fi
if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must start with 'v' (e.g. v0.0.1)"
exit 1
fi
cd "${PROJECT_ROOT}"
# first run the regular helm target to generate base templates
@@ -309,6 +333,48 @@ if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then
echo "Completed processing for deployment.yaml"
fi
# ? NOTE(Daniel): Fix args structure in deployment.yaml
if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then
echo "Fixing args structure in deployment.yaml"
touch "${HELM_DIR}/templates/deployment.yaml.tmp"
# process the file line by line
while IFS= read -r line; do
# look for the specific line pattern: "- args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }}"
if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*args:[[:space:]]*\{\{-.*toYaml.*Values\.controllerManager\.manager\.args.*\}\}[[:space:]]*$ ]]; then
# extract the base indentation (everything before the "- args:")
base_indent=$(echo "$line" | sed 's/^\([[:space:]]*\)-.*/\1/')
# replace with our multi-line structure
echo "${base_indent}- args:" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
echo "${base_indent} {{- toYaml .Values.controllerManager.manager.args | nindent 8 }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
echo "${base_indent} {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
echo "${base_indent} - --namespace={{ .Values.scopedNamespace }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
echo "${base_indent} {{- end }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
else
echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.tmp"
fi
done < "${HELM_DIR}/templates/deployment.yaml"
mv "${HELM_DIR}/templates/deployment.yaml.tmp" "${HELM_DIR}/templates/deployment.yaml"
echo "Completed args structure fix"
fi
# ? NOTE(Daniel): Processes values.yaml
if [ -f "${HELM_DIR}/values.yaml" ]; then
echo "Processing values.yaml file"
@@ -402,4 +468,20 @@ if [ -f "${HELM_DIR}/values.yaml" ]; then
echo "Completed processing for values.yaml"
fi
echo "Helm chart generation complete with custom templating applied."
echo "Helm chart generation complete with custom templating applied."
# For Linux vs macOS sed compatibility
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS version
sed -i '' 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
sed -i '' 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
else
# Linux version
sed -i 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
sed -i 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
fi
echo "Helm chart version updated to ${VERSION}"

View File

@@ -1,37 +0,0 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
PATH_TO_HELM_CHART="${SCRIPT_DIR}/../../helm-charts/secrets-operator"
VERSION=$1
VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') # needed to validate semver
if [ -z "$VERSION" ]; then
echo "Usage: $0 <version>"
exit 1
fi
if ! [[ "$VERSION_WITHOUT_V" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must follow semantic versioning (e.g. 0.0.1)"
exit 1
fi
if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must start with 'v' (e.g. v0.0.1)"
exit 1
fi
# For Linux vs macOS sed compatibility
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS version
sed -i '' -e '/repository: infisical\/kubernetes-operator/{n;s/tag: .*/tag: '"$VERSION"'/;}' "${PATH_TO_HELM_CHART}/values.yaml"
sed -i '' 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
sed -i '' 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
else
# Linux version
sed -i -e '/repository: infisical\/kubernetes-operator/{n;s/tag: .*/tag: '"$VERSION"'/;}' "${PATH_TO_HELM_CHART}/values.yaml"
sed -i 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
sed -i 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml"
fi

View File

@@ -31,16 +31,16 @@ import (
)
// namespace where the project is deployed in
const namespace = "k8-operator-system"
const namespace = "infisical-operator-system"
// serviceAccountName created for the project
const serviceAccountName = "k8-operator-controller-manager"
const serviceAccountName = "infisical-operator-controller-manager"
// metricsServiceName is the name of the metrics service of the project
const metricsServiceName = "k8-operator-controller-manager-metrics-service"
const metricsServiceName = "infisical-operator-controller-manager-metrics-service"
// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data
const metricsRoleBindingName = "k8-operator-metrics-binding"
const metricsRoleBindingName = "infisical-operator-metrics-binding"
var _ = Describe("Manager", Ordered, func() {
var controllerPodName string
@@ -173,7 +173,7 @@ var _ = Describe("Manager", Ordered, func() {
It("should ensure the metrics endpoint is serving metrics", func() {
By("creating a ClusterRoleBinding for the service account to allow access to metrics")
cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
"--clusterrole=k8-operator-metrics-reader",
"--clusterrole=infisical-operator-metrics-reader",
fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
)
_, err := utils.Run(cmd)