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
+2 -2
View File
@@ -13,9 +13,9 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes # This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version. # to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/) # Versions are expected to follow Semantic Versioning (https://semver.org/)
version: v0.9.5 version: v0.9.6
# This is the version number of the application being deployed. This version number should be # This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "v0.9.5" appVersion: "v0.9.6"
@@ -22,7 +22,11 @@ spec:
kubectl.kubernetes.io/default-container: manager kubectl.kubernetes.io/default-container: manager
spec: spec:
containers: containers:
- args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} - args:
{{- toYaml .Values.controllerManager.manager.args | nindent 8 }}
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
- --namespace={{ .Values.scopedNamespace }}
{{- end }}
command: command:
- /manager - /manager
env: env:
@@ -48,7 +48,9 @@ rules:
- apiGroups: - apiGroups:
- apps - apps
resources: resources:
- daemonsets
- deployments - deployments
- statefulsets
verbs: verbs:
- get - get
- list - list
+1 -1
View File
@@ -12,7 +12,7 @@ controllerManager:
readOnlyRootFilesystem: true readOnlyRootFilesystem: true
image: image:
repository: infisical/kubernetes-operator repository: infisical/kubernetes-operator
tag: v0.9.5 tag: v0.9.6
resources: resources:
limits: limits:
cpu: 500m cpu: 500m
+5 -5
View File
@@ -37,7 +37,7 @@ legacy-helm: manifests kustomize helmify
helm: manifests kustomize helmify helm: manifests kustomize helmify
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} 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 cd config/manager && $(KUSTOMIZE) edit set image controller=controller:latest # reset back
## Yaml for Kubectl ## 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. # The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
# CertManager is installed by default; skip with: # CertManager is installed by default; skip with:
# - CERT_MANAGER_INSTALL_SKIP=true # - CERT_MANAGER_INSTALL_SKIP=true
KIND_CLUSTER ?= k8-operator-test-e2e KIND_CLUSTER ?= infisical-operator-test-e2e
.PHONY: setup-test-e2e .PHONY: setup-test-e2e
setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist 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 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 # 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 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 create --name infisical-operator-builder
$(CONTAINER_TOOL) buildx use k8-operator-builder $(CONTAINER_TOOL) buildx use infisical-operator-builder
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - $(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 rm Dockerfile.cross
.PHONY: build-installer .PHONY: build-installer
+35 -11
View File
@@ -19,6 +19,7 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"flag" "flag"
"fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -30,6 +31,7 @@ import (
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme" clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime" 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/certwatcher"
"sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -63,7 +65,10 @@ func main() {
var probeAddr string var probeAddr string
var secureMetrics bool var secureMetrics bool
var enableHTTP2 bool var enableHTTP2 bool
var namespace string
var tlsOpts []func(*tls.Config) 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. "+ 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.") "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.") 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, Scheme: scheme,
Metrics: metricsServerOptions, Metrics: metricsServerOptions,
WebhookServer: webhookServer, WebhookServer: webhookServer,
@@ -196,32 +201,51 @@ func main() {
// if you are doing or is intended to do any operation such as perform cleanups // 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. // after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true, // 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 { if err != nil {
setupLog.Error(err, "unable to start manager") setupLog.Error(err, "unable to start manager")
os.Exit(1) os.Exit(1)
} }
if err := (&controller.InfisicalSecretReconciler{ if err := (&controller.InfisicalSecretReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log, BaseLogger: ctrl.Log,
Namespace: namespace,
IsNamespaceScoped: namespace != "",
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret") setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret")
os.Exit(1) os.Exit(1)
} }
if err := (&controller.InfisicalPushSecretReconciler{ if err := (&controller.InfisicalPushSecretReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log, IsNamespaceScoped: namespace != "",
Namespace: namespace,
BaseLogger: ctrl.Log,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret") setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret")
os.Exit(1) os.Exit(1)
} }
if err := (&controller.InfisicalDynamicSecretReconciler{ if err := (&controller.InfisicalDynamicSecretReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
BaseLogger: ctrl.Log, BaseLogger: ctrl.Log,
IsNamespaceScoped: namespace != "",
Namespace: namespace,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret") setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
os.Exit(1) os.Exit(1)
+9 -217
View File
@@ -1,12 +1,12 @@
# Adds namespace to all resources. # Adds namespace to all resources.
namespace: k8-operator-system namespace: infisical-operator-system
# Value of this field is prepended to the # Value of this field is prepended to the
# names of all resources, e.g. a deployment named # names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress". # "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace # Note that it should also match with the prefix (text before '-') of the namespace
# field above. # field above.
namePrefix: k8-operator- namePrefix: infisical-operator-
# Labels to add to all resources and selectors. # Labels to add to all resources and selectors.
#labels: #labels:
@@ -15,220 +15,12 @@ namePrefix: k8-operator-
# someName: someValue # someName: someValue
resources: resources:
- ../crd - ../crd
- ../rbac - ../rbac
- ../manager - ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in - metrics_service.yaml
# 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
# Uncomment the patches line if you enable Metrics
patches: patches:
# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. - path: manager_metrics_patch.yaml
# More info: https://book.kubebuilder.io/reference/metrics target:
- path: manager_metrics_patch.yaml kind: Deployment
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
+2
View File
@@ -40,7 +40,9 @@ rules:
- apiGroups: - apiGroups:
- apps - apps
resources: resources:
- daemonsets
- deployments - deployments
- statefulsets
verbs: verbs:
- get - get
- list - list
@@ -42,9 +42,11 @@ import (
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object // InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
type InfisicalDynamicSecretReconciler struct { type InfisicalDynamicSecretReconciler struct {
client.Client client.Client
BaseLogger logr.Logger BaseLogger logr.Logger
Scheme *runtime.Scheme Scheme *runtime.Scheme
Random *rand.Rand Random *rand.Rand
Namespace string
IsNamespaceScoped bool
} }
var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) 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 // 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) err := handler.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap)
@@ -126,7 +128,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
} }
// Get modified/default config // Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil { if err != nil {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{ return ctrl.Result{
@@ -135,7 +137,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
} }
// Initialize the business logic handler // 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 // Setup API configuration through business logic
err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig) err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig)
@@ -169,7 +171,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct
}, nil }, 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) handler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err)
if err != nil { if err != nil {
@@ -45,9 +45,10 @@ import (
// InfisicalPushSecretReconciler reconciles a InfisicalPushSecretSecret object // InfisicalPushSecretReconciler reconciles a InfisicalPushSecretSecret object
type InfisicalPushSecretReconciler struct { type InfisicalPushSecretReconciler struct {
client.Client client.Client
IsNamespaceScoped bool
BaseLogger logr.Logger BaseLogger logr.Logger
Scheme *runtime.Scheme Scheme *runtime.Scheme
IsNamespaceScoped bool
Namespace string
} }
var infisicalPushSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) 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 // Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil { if err != nil {
if requeueTime != 0 { if requeueTime != 0 {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
@@ -41,8 +41,10 @@ import (
// InfisicalSecretReconciler reconciles a InfisicalSecret object // InfisicalSecretReconciler reconciles a InfisicalSecret object
type InfisicalSecretReconciler struct { type InfisicalSecretReconciler struct {
client.Client client.Client
BaseLogger logr.Logger BaseLogger logr.Logger
Scheme *runtime.Scheme Scheme *runtime.Scheme
Namespace string
IsNamespaceScoped bool
} }
var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) 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) 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,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/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update //+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 // Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state. // 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 // Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped)
if err != nil { if err != nil {
logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{ return ctrl.Result{
@@ -147,7 +156,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
} }
// Initialize the business logic handler // 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 // Setup API configuration through business logic
err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig) err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig)
@@ -177,7 +186,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
}, nil }, 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) handler.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
if err != nil { if err != nil {
@@ -7,6 +7,7 @@ import (
"github.com/Infisical/infisical/k8-operator/api/v1alpha1" "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/internal/constants" "github.com/Infisical/infisical/k8-operator/internal/constants"
"github.com/Infisical/infisical/k8-operator/internal/util"
"github.com/go-logr/logr" "github.com/go-logr/logr"
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@@ -19,7 +20,7 @@ import (
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret" 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 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{} listOfDeployments := &v1.DeploymentList{}
err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
@@ -47,6 +48,10 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll
managedKubeSecret := &corev1.Secret{} managedKubeSecret := &corev1.Secret{}
err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret) err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
if err != nil { 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) 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 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 { for _, managedSecret := range managedSecrets {
_, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret) _, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret, isNamespaceScoped)
if err != nil { if err != nil {
logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName)) logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName))
return 0, err return 0, err
@@ -259,11 +264,17 @@ func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, l
return nil 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 // default key values
defaultConfigMapData := make(map[string]string) defaultConfigMapData := make(map[string]string)
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN 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{} kubeConfigMap := &corev1.ConfigMap{}
err := client.Get(ctx, types.NamespacedName{ err := client.Get(ctx, types.NamespacedName{
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
@@ -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
}
}
@@ -19,15 +19,17 @@ import (
type InfisicalDynamicSecretHandler struct { type InfisicalDynamicSecretHandler struct {
client.Client client.Client
Scheme *runtime.Scheme Scheme *runtime.Scheme
Random *rand.Rand 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{ return &InfisicalDynamicSecretHandler{
Client: client, Client: client,
Scheme: scheme, Scheme: scheme,
Random: rand.New(rand.NewSource(time.Now().UnixNano())), 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) 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 { if err != nil {
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) 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) { func (h *InfisicalDynamicSecretHandler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) {
reconciler := &InfisicalDynamicSecretReconciler{ reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
Random: h.Random, Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
return reconciler.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) 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 { func (h *InfisicalDynamicSecretHandler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error {
reconciler := &InfisicalDynamicSecretReconciler{ reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
Random: h.Random, Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap)
} }
func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) { func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) {
reconciler := &InfisicalDynamicSecretReconciler{ reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
Random: h.Random, Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn) reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn)
} }
func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) { func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) {
reconciler := &InfisicalDynamicSecretReconciler{ reconciler := &InfisicalDynamicSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
Random: h.Random, Random: h.Random,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn) reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn)
} }
@@ -2,7 +2,6 @@ package infisicaldynamicsecret
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"strings" "strings"
@@ -27,8 +26,9 @@ import (
type InfisicalDynamicSecretReconciler struct { type InfisicalDynamicSecretReconciler struct {
client.Client client.Client
Scheme *runtime.Scheme Scheme *runtime.Scheme
Random *rand.Rand Random *rand.Rand
IsNamespaceScoped bool
} }
func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error { 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 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 { func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
var resourceVariables util.ResourceVariables var resourceVariables util.ResourceVariables
@@ -254,7 +224,10 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con
infisicalClient := resourceVariables.InfisicalClient infisicalClient := resourceVariables.InfisicalClient
logger.Info("Authenticating for lease revocation") 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 { if err != nil {
return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err) 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 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) return fmt.Errorf("unable to fetch destination secret [err=%s]", err)
} }
@@ -318,7 +294,10 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
if authDetails.AuthStrategy == "" { if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate") 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 { if err != nil {
return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err) return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err)
@@ -337,6 +316,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
}) })
if err != nil { 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) { if k8Errors.IsNotFound(err) {
annotationValue := "" annotationValue := ""
@@ -352,6 +334,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
}) })
if err != nil { 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) return nextReconcile, fmt.Errorf("unable to fetch destination secret after creation [err=%s]", err)
} }
@@ -49,6 +49,10 @@ func (h *InfisicalPushSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) 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 { if err != nil {
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
} }
@@ -3,7 +3,6 @@ package infisicalpushsecret
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"strings" "strings"
tpl "text/template" tpl "text/template"
@@ -30,36 +29,6 @@ type InfisicalPushSecretReconciler struct {
IsNamespaceScoped bool 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 { func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables {
var resourceVariables util.ResourceVariables var resourceVariables util.ResourceVariables
@@ -192,7 +161,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
if authDetails.AuthStrategy == "" { if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate") 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) r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
if err != nil { if err != nil {
@@ -215,6 +187,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
}) })
if err != nil { 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) 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 == "" { if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate") 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) r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err)
if err != nil { if err != nil {
@@ -17,13 +17,15 @@ import (
type InfisicalSecretHandler struct { type InfisicalSecretHandler struct {
client.Client 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{ return &InfisicalSecretHandler{
Client: client, Client: client,
Scheme: scheme, Scheme: scheme,
IsNamespaceScoped: isNamespaceScoped,
} }
} }
@@ -48,6 +50,9 @@ func (h *InfisicalSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx con
} }
if err != nil { 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) 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) { 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{ reconciler := &InfisicalSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
return reconciler.ReconcileInfisicalSecret(ctx, logger, infisicalSecret, managedKubeSecretReferences, managedKubeConfigMapReferences, resourceVariablesMap) 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) { func (h *InfisicalSecretHandler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) {
reconciler := &InfisicalSecretReconciler{ reconciler := &InfisicalSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn) reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn)
} }
func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) { func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) {
reconciler := &InfisicalSecretReconciler{ reconciler := &InfisicalSecretReconciler{
Client: h.Client, Client: h.Client,
Scheme: h.Scheme, Scheme: h.Scheme,
IsNamespaceScoped: h.IsNamespaceScoped,
} }
reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn) reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn)
} }
@@ -32,59 +32,8 @@ const FINALIZER_NAME = "secrets.finalizers.infisical.com"
type InfisicalSecretReconciler struct { type InfisicalSecretReconciler struct {
client.Client client.Client
Scheme *runtime.Scheme Scheme *runtime.Scheme
} IsNamespaceScoped bool
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")
} }
func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) {
@@ -105,11 +54,14 @@ func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.
Name: secretName, Name: secretName,
}) })
if k8Errors.IsNotFound(err) { if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return "", nil return "", nil
} }
if err != 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) 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 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. // 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. // 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) { 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{ serviceAccountCredsFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
Namespace: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace, Namespace: secretNamespace,
Name: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName, Name: secretName,
}) })
if k8Errors.IsNotFound(err) { if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") {
return model.ServiceAccountDetails{}, nil return model.ServiceAccountDetails{}, nil
} }
if err != 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) 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 == "" { if authDetails.AuthStrategy == "" {
logger.Info("No authentication strategy found. Attempting to authenticate") 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) r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err)
if err != nil { if err != nil {
@@ -533,6 +476,9 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
}) })
if err != nil && !k8Errors.IsNotFound(err) { 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) 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 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) return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err)
} }
+83 -10
View File
@@ -16,7 +16,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "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 { if autoCreateServiceAccountToken {
restClient, err := GetRestClientFromClient() restClient, err := GetRestClientFromClient()
@@ -57,6 +57,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc
serviceAccount := &corev1.ServiceAccount{} serviceAccount := &corev1.ServiceAccount{}
err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount) err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount)
if err != nil { 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 return "", err
} }
@@ -69,6 +72,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc
secret := &corev1.Secret{} secret := &corev1.Secret{}
err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret) err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret)
if err != nil { 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 return "", err
} }
@@ -127,7 +133,7 @@ type AuthenticationDetails struct {
var ErrAuthNotApplicable = errors.New("authentication not applicable") 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 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{ universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace, SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace,
SecretName: universalAuthSpec.CredentialsRef.SecretName, SecretName: universalAuthSpec.CredentialsRef.SecretName,
}) }, isNamespaceScoped)
if err != nil { if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) 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 }, 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 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{ ldapAuthKubeSecret, err := GetInfisicalLdapAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace, SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace,
SecretName: ldapAuthSpec.CredentialsRef.SecretName, SecretName: ldapAuthSpec.CredentialsRef.SecretName,
}) }, isNamespaceScoped)
if err != nil { if err != nil {
return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) 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 }, 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 var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails
switch secretCrd.Type { switch secretCrd.Type {
@@ -312,6 +326,7 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s
kubernetesAuthSpec.ServiceAccountRef.Name, kubernetesAuthSpec.ServiceAccountRef.Name,
kubernetesAuthSpec.AutoCreateServiceAccountToken, kubernetesAuthSpec.AutoCreateServiceAccountToken,
kubernetesAuthSpec.ServiceAccountTokenAudiences, kubernetesAuthSpec.ServiceAccountTokenAudiences,
isNamespaceScoped,
) )
if err != nil { 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{} awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{}
switch secretCrd.Type { 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{} azureAuthSpec := v1alpha1.AzureAuthDetails{}
switch secretCrd.Type { 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{} gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{}
switch secretCrd.Type { 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{} gcpIamSpec := v1alpha1.GcpIamAuthDetails{}
switch secretCrd.Type { switch secretCrd.Type {
@@ -555,3 +570,61 @@ func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secre
SecretType: secretCrd.Type, SecretType: secretCrd.Type,
}, nil }, 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")
}
+4
View File
@@ -54,3 +54,7 @@ func AppendAPIEndpoint(address string) string {
} }
return address + "/api" return address + "/api"
} }
func IsNamespaceScopedError(err error, isNamespaceScoped bool) bool {
return isNamespaceScoped && err != nil && strings.Contains(err.Error(), "unknown namespace for the cache")
}
+70 -4
View File
@@ -3,8 +3,10 @@ package util
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/Infisical/infisical/k8-operator/api/v1alpha1" "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
"github.com/Infisical/infisical/k8-operator/internal/constants"
"github.com/Infisical/infisical/k8-operator/internal/model" "github.com/Infisical/infisical/k8-operator/internal/model"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
k8Errors "k8s.io/apimachinery/pkg/api/errors" k8Errors "k8s.io/apimachinery/pkg/api/errors"
@@ -41,13 +43,11 @@ func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient clie
return kubeConfigMap, err 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{ universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: universalAuthRef.SecretNamespace, Namespace: universalAuthRef.SecretNamespace,
Name: universalAuthRef.SecretName, Name: universalAuthRef.SecretName,
// Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace,
// Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName,
}) })
if k8Errors.IsNotFound(err) { if k8Errors.IsNotFound(err) {
@@ -55,6 +55,9 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie
} }
if err != nil { 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) 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{ ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{
Namespace: ldapAuthRef.SecretNamespace, Namespace: ldapAuthRef.SecretNamespace,
@@ -77,6 +80,9 @@ func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient cl
} }
if err != nil { 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) 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 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
}
+82
View File
@@ -1,12 +1,36 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) 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) PROJECT_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
HELM_DIR="${PROJECT_ROOT}/../helm-charts/secrets-operator" HELM_DIR="${PROJECT_ROOT}/../helm-charts/secrets-operator"
LOCALBIN="${PROJECT_ROOT}/bin" LOCALBIN="${PROJECT_ROOT}/bin"
KUSTOMIZE="${LOCALBIN}/kustomize" KUSTOMIZE="${LOCALBIN}/kustomize"
HELMIFY="${LOCALBIN}/helmify" 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}" cd "${PROJECT_ROOT}"
# first run the regular helm target to generate base templates # 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" echo "Completed processing for deployment.yaml"
fi 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 # ? NOTE(Daniel): Processes values.yaml
if [ -f "${HELM_DIR}/values.yaml" ]; then if [ -f "${HELM_DIR}/values.yaml" ]; then
echo "Processing values.yaml file" echo "Processing values.yaml file"
@@ -403,3 +469,19 @@ if [ -f "${HELM_DIR}/values.yaml" ]; then
fi 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}"
-37
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
+5 -5
View File
@@ -31,16 +31,16 @@ import (
) )
// namespace where the project is deployed in // namespace where the project is deployed in
const namespace = "k8-operator-system" const namespace = "infisical-operator-system"
// serviceAccountName created for the project // 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 // 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 // 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 _ = Describe("Manager", Ordered, func() {
var controllerPodName string var controllerPodName string
@@ -173,7 +173,7 @@ var _ = Describe("Manager", Ordered, func() {
It("should ensure the metrics endpoint is serving metrics", func() { It("should ensure the metrics endpoint is serving metrics", func() {
By("creating a ClusterRoleBinding for the service account to allow access to metrics") By("creating a ClusterRoleBinding for the service account to allow access to metrics")
cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName,
"--clusterrole=k8-operator-metrics-reader", "--clusterrole=infisical-operator-metrics-reader",
fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName),
) )
_, err := utils.Run(cmd) _, err := utils.Run(cmd)