diff --git a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
index 50f07bb76..936a37303 100644
--- a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
+++ b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
@@ -34,7 +34,7 @@ Before applying the InfisicalPushSecret CRD, you need to create a Kubernetes sec
metadata:
name: infisical-push-secret-demo
spec:
- resyncInterval: 1m
+ resyncInterval: 1m # Remove this field to disable automatic reconciliation of the InfisicalPushSecret CRD.
hostAPI: https://app.infisical.com/api
# Optional, defaults to no replacement.
@@ -124,7 +124,9 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
- The `resyncInterval` is a string-formatted duration that defines the time between each resync.
+ The `resyncInterval` is a string-formatted duration that defines the time between each resync. The field is optional, and will default to no automatic resync if not defined.
+
+ If you don't want to automatically reconcile the InfisicalPushSecret CRD on an interval, you can remove the `resyncInterval` field entirely from your InfisicalPushSecret CRD.
The format of the field is `[duration][unit]` where `duration` is a number and `unit` is a string representing the unit of time.
@@ -459,6 +461,126 @@ Using Go templates, you can format, combine, and create new key-value pairs of s
Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
+## Using generators to push secrets
+
+Generators are a feature of the Infisical secrets operator that allows you to generate secrets on-reconcile and push them to Infisical. This is useful for secret rotation purposes, and fully operator-managed secrets.
+A generator is a custom resource that is installed on the cluster that defines the logic for generating a secret.
+
+Generators don't keep track of the secrets they generate, which means that on each reconciliation, a new value will be created and pushed.
+For this reason you may want to disable automatic reconciliation of the InfisicalPushSecret CRD. You can do this by removing `resyncInterval` from the InfisicalPushSecret CRD.
+
+**Supported generators**:
+- `Password`: Generates a random password of string format.
+- `UUID`: Generates a random v4 UUID.
+
+To use a generator, you must specify at least one generator in the `push.generators[]` field. An example of a generator usage can be seen here:
+
+
+ Define a generator in the `push.generators[]` field.
+
+
+ The name of the secret that will be created in Infisical.
+
+
+
+ The reference to the generator resource.
+
+ Valid fields:
+ - `kind`: The kind of the generator resource, must match the generator kind.
+ - `name`: The name of the generator resource.
+
+
+
+```yaml
+ push:
+ secret:
+ secretName: push-secret-source-secret
+ secretNamespace: dev
+ generators:
+ - destinationSecretName: password-generator # Name of the secret that will be created in Infisical
+ generatorRef:
+ kind: Password|UUID # Kind of the resource, must match the generator kind.
+ name: custom-generator # Name of the generator resource
+```
+
+
+
+
+ The Password generator is a custom resource that is installed on the cluster that defines the logic for generating a password.
+
+
+ - `kind`: The kind of the generator resource, must match the generator kind. For the Password generator, the kind is `Password`.
+ - `generator.passwordSpec`: The spec of the password generator.
+
+
+ - `length`: The length of the password.
+ - `digits`: The number of digits in the password.
+ - `symbols`: The number of symbols in the password.
+ - `symbolCharacters`: The characters to use for the symbols in the password.
+ - `noUpper`: Whether to include uppercase letters in the password.
+ - `allowRepeat`: Whether to allow repeating characters in the password.
+
+
+
+ ```yaml password-cluster-generator.yaml
+ apiVersion: secrets.infisical.com/v1alpha1
+ kind: ClusterGenerator
+ metadata:
+ name: password-generator
+ spec:
+ kind: Password
+ generator:
+ passwordSpec:
+ length: 10
+ digits: 5
+ symbols: 5
+ symbolCharacters: "-_$@"
+ noUpper: false
+ allowRepeat: true
+ ```
+
+ Example InfisicalPushSecret CRD using the Password generator:
+ ```yaml infisical-push-secret-crd.yaml
+ push:
+ generators:
+ - destinationSecretName: password-generator-test
+ generatorRef:
+ kind: Password
+ name: password-generator
+ ```
+
+
+ The UUID generator is a custom resource that is installed on the cluster that defines the logic for generating a UUID.
+
+ - `kind`: The kind of the generator resource, must match the generator kind. For the UUID generator, the kind is `UUID`.
+ - `generator.uuidSpec`: The spec of the UUID generator. For UUID's, this can be left empty.
+
+
+ ```yaml uuid-cluster-generator.yaml
+ apiVersion: secrets.infisical.com/v1alpha1
+ kind: ClusterGenerator
+ metadata:
+ name: uuid-generator
+ spec:
+ kind: UUID
+ generator:
+ uuidSpec:
+ ```
+
+ Example InfisicalPushSecret CRD using the UUID generator:
+ ```yaml infisical-push-secret-crd.yaml
+ push:
+ generators:
+ - destinationSecretName: uuid-generator-test
+ generatorRef:
+ kind: UUID
+ name: uuid-generator
+ ```
+
+
+
+
+
## Applying the InfisicalPushSecret CRD to your cluster
Once you have configured the `InfisicalPushSecret` CRD with the required fields, you can apply it to your cluster.
diff --git a/k8-operator/api/v1alpha1/generators.go b/k8-operator/api/v1alpha1/generators.go
new file mode 100644
index 000000000..0f6d86c2d
--- /dev/null
+++ b/k8-operator/api/v1alpha1/generators.go
@@ -0,0 +1,152 @@
+/*
+Copyright 2022.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// GeneratorKind represents a kind of generator.
+// +kubebuilder:validation:Enum=Password;UUID
+type GeneratorKind string
+
+const (
+ GeneratorKindPassword GeneratorKind = "Password"
+ GeneratorKindUUID GeneratorKind = "UUID"
+)
+
+type ClusterGeneratorSpec struct {
+ // Kind the kind of this generator.
+ Kind GeneratorKind `json:"kind"`
+
+ // Generator the spec for this generator, must match the kind.
+ Generator GeneratorSpec `json:"generator,omitempty"`
+}
+
+type GeneratorSpec struct {
+ // +kubebuilder:validation:Optional
+ PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"`
+ // +kubebuilder:validation:Optional
+ UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"`
+}
+
+// ClusterGenerator represents a cluster-wide generator
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Cluster
+type ClusterGenerator struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec ClusterGeneratorSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// ClusterGeneratorList contains a list of ClusterGenerator resources.
+type ClusterGeneratorList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []ClusterGenerator `json:"items"`
+}
+
+// ! UUID Generator
+
+// UUIDSpec controls the behavior of the uuid generator.
+type UUIDSpec struct{}
+
+// UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216).
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+type UUID struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec UUIDSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// UUIDList contains a list of UUID resources.
+type UUIDList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []UUID `json:"items"`
+}
+
+// ! Password Generator
+
+// PasswordSpec controls the behavior of the password generator.
+type PasswordSpec struct {
+ // Length of the password to be generated.
+ // Defaults to 24
+ // +kubebuilder:validation:Optional
+ // +kubebuilder:default=24
+ Length int `json:"length"`
+
+ // digits specifies the number of digits in the generated
+ // password. If omitted it defaults to 25% of the length of the password
+ Digits *int `json:"digits,omitempty"`
+
+ // symbols specifies the number of symbol characters in the generated
+ // password. If omitted it defaults to 25% of the length of the password
+ Symbols *int `json:"symbols,omitempty"`
+
+ // symbolCharacters specifies the special characters that should be used
+ // in the generated password.
+ SymbolCharacters *string `json:"symbolCharacters,omitempty"`
+
+ // Set noUpper to disable uppercase characters
+ // +kubebuilder:validation:Optional
+ // +kubebuilder:default=false
+ NoUpper bool `json:"noUpper"`
+
+ // set allowRepeat to true to allow repeating characters.
+ // +kubebuilder:validation:Optional
+ // +kubebuilder:default=false
+ AllowRepeat bool `json:"allowRepeat"`
+}
+
+// Password generates a random password based on the
+// configuration parameters in spec.
+// You can specify the length, characterset and other attributes.
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:resource:scope=Namespaced
+type Password struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec PasswordSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// PasswordList contains a list of Password resources.
+type PasswordList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []Password `json:"items"`
+}
+
+func init() {
+ SchemeBuilder.Register(&Password{}, &PasswordList{})
+ SchemeBuilder.Register(&UUID{}, &UUIDList{})
+ SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{})
+}
diff --git a/k8-operator/api/v1alpha1/infisicalpushsecret_types.go b/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
index 5a7438881..8958c714d 100644
--- a/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
+++ b/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
@@ -29,9 +29,28 @@ type InfisicalPushSecretSecretSource struct {
Template *SecretTemplate `json:"template,omitempty"`
}
-type SecretPush struct {
+type GeneratorRef struct {
+ // Specify the Kind of the generator resource
+ // +kubebuilder:validation:Enum=Password;UUID
// +kubebuilder:validation:Required
- Secret InfisicalPushSecretSecretSource `json:"secret"`
+ Kind GeneratorKind `json:"kind"`
+
+ // +kubebuilder:validation:Required
+ Name string `json:"name"`
+}
+
+type SecretPushGenerator struct {
+ // +kubebuilder:validation:Required
+ DestinationSecretName string `json:"destinationSecretName"`
+ // +kubebuilder:validation:Required
+ GeneratorRef GeneratorRef `json:"generatorRef"`
+}
+
+type SecretPush struct {
+ // +kubebuilder:validation:Optional
+ Secret *InfisicalPushSecretSecretSource `json:"secret,omitempty"`
+ // +kubebuilder:validation:Optional
+ Generators []SecretPushGenerator `json:"generators,omitempty"`
}
// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
@@ -52,7 +71,8 @@ type InfisicalPushSecretSpec struct {
// +kubebuilder:validation:Required
Push SecretPush `json:"push"`
- ResyncInterval string `json:"resyncInterval"`
+ // +kubebuilder:validation:Optional
+ ResyncInterval *string `json:"resyncInterval,omitempty"`
// Infisical host to pull secrets from
// +kubebuilder:validation:Optional
diff --git a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
index 4958e9c76..382b0e8cd 100644
--- a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
@@ -96,6 +96,80 @@ func (in *CaReference) DeepCopy() *CaReference {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGenerator) DeepCopyInto(out *ClusterGenerator) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGenerator.
+func (in *ClusterGenerator) DeepCopy() *ClusterGenerator {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGenerator)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterGenerator) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGeneratorList) DeepCopyInto(out *ClusterGeneratorList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]ClusterGenerator, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGeneratorList.
+func (in *ClusterGeneratorList) DeepCopy() *ClusterGeneratorList {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGeneratorList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterGeneratorList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGeneratorSpec) DeepCopyInto(out *ClusterGeneratorSpec) {
+ *out = *in
+ in.Generator.DeepCopyInto(&out.Generator)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGeneratorSpec.
+func (in *ClusterGeneratorSpec) DeepCopy() *ClusterGeneratorSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGeneratorSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DynamicSecretDetails) DeepCopyInto(out *DynamicSecretDetails) {
*out = *in
@@ -143,6 +217,46 @@ func (in *GcpIamAuthDetails) DeepCopy() *GcpIamAuthDetails {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GeneratorRef) DeepCopyInto(out *GeneratorRef) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratorRef.
+func (in *GeneratorRef) DeepCopy() *GeneratorRef {
+ if in == nil {
+ return nil
+ }
+ out := new(GeneratorRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GeneratorSpec) DeepCopyInto(out *GeneratorSpec) {
+ *out = *in
+ if in.PasswordSpec != nil {
+ in, out := &in.PasswordSpec, &out.PasswordSpec
+ *out = new(PasswordSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.UUIDSpec != nil {
+ in, out := &in.UUIDSpec, &out.UUIDSpec
+ *out = new(UUIDSpec)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratorSpec.
+func (in *GeneratorSpec) DeepCopy() *GeneratorSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(GeneratorSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GenericAwsIamAuth) DeepCopyInto(out *GenericAwsIamAuth) {
*out = *in
@@ -483,6 +597,11 @@ func (in *InfisicalPushSecretSpec) DeepCopyInto(out *InfisicalPushSecretSpec) {
out.Destination = in.Destination
in.Authentication.DeepCopyInto(&out.Authentication)
in.Push.DeepCopyInto(&out.Push)
+ if in.ResyncInterval != nil {
+ in, out := &in.ResyncInterval, &out.ResyncInterval
+ *out = new(string)
+ **out = **in
+ }
out.TLS = in.TLS
}
@@ -746,10 +865,107 @@ func (in *ManagedKubeSecretConfig) DeepCopy() *ManagedKubeSecretConfig {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Password) DeepCopyInto(out *Password) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Password.
+func (in *Password) DeepCopy() *Password {
+ if in == nil {
+ return nil
+ }
+ out := new(Password)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Password) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PasswordList) DeepCopyInto(out *PasswordList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]Password, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordList.
+func (in *PasswordList) DeepCopy() *PasswordList {
+ if in == nil {
+ return nil
+ }
+ out := new(PasswordList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PasswordList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PasswordSpec) DeepCopyInto(out *PasswordSpec) {
+ *out = *in
+ if in.Digits != nil {
+ in, out := &in.Digits, &out.Digits
+ *out = new(int)
+ **out = **in
+ }
+ if in.Symbols != nil {
+ in, out := &in.Symbols, &out.Symbols
+ *out = new(int)
+ **out = **in
+ }
+ if in.SymbolCharacters != nil {
+ in, out := &in.SymbolCharacters, &out.SymbolCharacters
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PasswordSpec.
+func (in *PasswordSpec) DeepCopy() *PasswordSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(PasswordSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretPush) DeepCopyInto(out *SecretPush) {
*out = *in
- in.Secret.DeepCopyInto(&out.Secret)
+ if in.Secret != nil {
+ in, out := &in.Secret, &out.Secret
+ *out = new(InfisicalPushSecretSecretSource)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Generators != nil {
+ in, out := &in.Generators, &out.Generators
+ *out = make([]SecretPushGenerator, len(*in))
+ copy(*out, *in)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPush.
@@ -762,6 +978,22 @@ func (in *SecretPush) DeepCopy() *SecretPush {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SecretPushGenerator) DeepCopyInto(out *SecretPushGenerator) {
+ *out = *in
+ out.GeneratorRef = in.GeneratorRef
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPushGenerator.
+func (in *SecretPushGenerator) DeepCopy() *SecretPushGenerator {
+ if in == nil {
+ return nil
+ }
+ out := new(SecretPushGenerator)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretScopeInWorkspace) DeepCopyInto(out *SecretScopeInWorkspace) {
*out = *in
@@ -848,6 +1080,79 @@ func (in *TLSConfig) DeepCopy() *TLSConfig {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUID) DeepCopyInto(out *UUID) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUID.
+func (in *UUID) DeepCopy() *UUID {
+ if in == nil {
+ return nil
+ }
+ out := new(UUID)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUID) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDList) DeepCopyInto(out *UUIDList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]UUID, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDList.
+func (in *UUIDList) DeepCopy() *UUIDList {
+ if in == nil {
+ return nil
+ }
+ out := new(UUIDList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUIDList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDSpec) DeepCopyInto(out *UUIDSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDSpec.
+func (in *UUIDSpec) DeepCopy() *UUIDSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(UUIDSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *UniversalAuthDetails) DeepCopyInto(out *UniversalAuthDetails) {
*out = *in
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml
new file mode 100644
index 000000000..c4a9eb168
--- /dev/null
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml
@@ -0,0 +1,90 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.10.0
+ creationTimestamp: null
+ name: clustergenerators.secrets.infisical.com
+spec:
+ group: secrets.infisical.com
+ names:
+ kind: ClusterGenerator
+ listKind: ClusterGeneratorList
+ plural: clustergenerators
+ singular: clustergenerator
+ scope: Cluster
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: ClusterGenerator represents a cluster-wide generator
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ generator:
+ description: Generator the spec for this generator, must match the
+ kind.
+ properties:
+ passwordSpec:
+ description: PasswordSpec controls the behavior of the password
+ generator.
+ properties:
+ allowRepeat:
+ default: false
+ description: set allowRepeat to true to allow repeating characters.
+ type: boolean
+ digits:
+ description: digits specifies the number of digits in the
+ generated password. If omitted it defaults to 25% of the
+ length of the password
+ type: integer
+ length:
+ default: 24
+ description: Length of the password to be generated. Defaults
+ to 24
+ type: integer
+ noUpper:
+ default: false
+ description: Set noUpper to disable uppercase characters
+ type: boolean
+ symbolCharacters:
+ description: symbolCharacters specifies the special characters
+ that should be used in the generated password.
+ type: string
+ symbols:
+ description: symbols specifies the number of symbol characters
+ in the generated password. If omitted it defaults to 25%
+ of the length of the password
+ type: integer
+ type: object
+ uuidSpec:
+ description: UUIDSpec controls the behavior of the uuid generator.
+ type: object
+ type: object
+ kind:
+ description: Kind the kind of this generator.
+ enum:
+ - Password
+ - UUID
+ type: string
+ required:
+ - kind
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
index 37df72854..9d7e2adbd 100644
--- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
@@ -142,6 +142,34 @@ spec:
type: string
push:
properties:
+ generators:
+ items:
+ properties:
+ destinationSecretName:
+ type: string
+ generatorRef:
+ properties:
+ kind:
+ allOf:
+ - enum:
+ - Password
+ - UUID
+ - enum:
+ - Password
+ - UUID
+ description: Specify the Kind of the generator resource
+ type: string
+ name:
+ type: string
+ required:
+ - kind
+ - name
+ type: object
+ required:
+ - destinationSecretName
+ - generatorRef
+ type: object
+ type: array
secret:
properties:
secretName:
@@ -168,8 +196,6 @@ spec:
- secretName
- secretNamespace
type: object
- required:
- - secret
type: object
resyncInterval:
type: string
@@ -200,7 +226,6 @@ spec:
required:
- destination
- push
- - resyncInterval
type: object
status:
description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml
new file mode 100644
index 000000000..dc14f2bf0
--- /dev/null
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml
@@ -0,0 +1,69 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.10.0
+ creationTimestamp: null
+ name: passwords.secrets.infisical.com
+spec:
+ group: secrets.infisical.com
+ names:
+ kind: Password
+ listKind: PasswordList
+ plural: passwords
+ singular: password
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: Password generates a random password based on the configuration
+ parameters in spec. You can specify the length, characterset and other attributes.
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: PasswordSpec controls the behavior of the password generator.
+ properties:
+ allowRepeat:
+ default: false
+ description: set allowRepeat to true to allow repeating characters.
+ type: boolean
+ digits:
+ description: digits specifies the number of digits in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ length:
+ default: 24
+ description: Length of the password to be generated. Defaults to 24
+ type: integer
+ noUpper:
+ default: false
+ description: Set noUpper to disable uppercase characters
+ type: boolean
+ symbolCharacters:
+ description: symbolCharacters specifies the special characters that
+ should be used in the generated password.
+ type: string
+ symbols:
+ description: symbols specifies the number of symbol characters in
+ the generated password. If omitted it defaults to 25% of the length
+ of the password
+ type: integer
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml
new file mode 100644
index 000000000..495b5b276
--- /dev/null
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml
@@ -0,0 +1,42 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.10.0
+ creationTimestamp: null
+ name: uuids.secrets.infisical.com
+spec:
+ group: secrets.infisical.com
+ names:
+ kind: UUID
+ listKind: UUIDList
+ plural: uuids
+ singular: uuid
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216).
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: UUIDSpec controls the behavior of the uuid generator.
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/k8-operator/config/crd/kustomization.yaml b/k8-operator/config/crd/kustomization.yaml
index ea6db574a..02e3fad98 100644
--- a/k8-operator/config/crd/kustomization.yaml
+++ b/k8-operator/config/crd/kustomization.yaml
@@ -5,6 +5,7 @@ resources:
- bases/secrets.infisical.com_infisicalsecrets.yaml
- bases/secrets.infisical.com_infisicalpushsecrets.yaml
- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml
+ - bases/secrets.infisical.com_clustergenerators.yaml
#+kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
diff --git a/k8-operator/config/rbac/role.yaml b/k8-operator/config/rbac/role.yaml
index 542face87..ea2fbada3 100644
--- a/k8-operator/config/rbac/role.yaml
+++ b/k8-operator/config/rbac/role.yaml
@@ -74,6 +74,18 @@ rules:
- tokenreviews
verbs:
- create
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - clustergenerators
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
- apiGroups:
- secrets.infisical.com
resources:
diff --git a/k8-operator/config/samples/crd/pushsecret/cluster-password-generator.yml b/k8-operator/config/samples/crd/pushsecret/cluster-password-generator.yml
new file mode 100644
index 000000000..ce3c8c087
--- /dev/null
+++ b/k8-operator/config/samples/crd/pushsecret/cluster-password-generator.yml
@@ -0,0 +1,14 @@
+apiVersion: secrets.infisical.com/v1alpha1
+kind: ClusterGenerator
+metadata:
+ name: password-generator
+spec:
+ kind: Password
+ generator:
+ passwordSpec:
+ length: 10
+ digits: 5
+ symbols: 5
+ symbolCharacters: "-_$@"
+ noUpper: false
+ allowRepeat: true
diff --git a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go b/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go
index 861880d95..6e3b135e4 100644
--- a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go
+++ b/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go
@@ -393,7 +393,7 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c
// Max TTL
if infisicalDynamicSecret.Status.MaxTTL != "" {
- maxTTLDuration, err := util.ConvertIntervalToDuration(infisicalDynamicSecret.Status.MaxTTL)
+ maxTTLDuration, err := util.ConvertIntervalToDuration(&infisicalDynamicSecret.Status.MaxTTL)
if err != nil {
return defaultNextReconcile, fmt.Errorf("unable to parse MaxTTL duration: %w", err)
}
diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
index ebf537a63..47c55d698 100644
--- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
+++ b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
@@ -51,7 +51,7 @@ func (r *InfisicalPushSecretReconciler) GetLogger(req ctrl.Request) logr.Logger
//+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
-
+// +kubebuilder:rbac:groups=secrets.infisical.com,resources=clustergenerators,verbs=get;list;watch;create;update;patch;delete
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// For more details, check Reconcile and its Result here:
@@ -108,23 +108,30 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
return ctrl.Result{}, nil
}
- if infisicalPushSecretCRD.Spec.ResyncInterval != "" {
+ if infisicalPushSecretCRD.Spec.Push.Secret == nil && infisicalPushSecretCRD.Spec.Push.Generators == nil {
+ logger.Info("No secret or generators found, skipping reconciliation. Please define ")
+ return ctrl.Result{}, nil
+ }
- duration, err := util.ConvertIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
+ duration, err := util.ConvertIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
- if err != nil {
+ if err != nil {
+ // if resyncInterval is nil, we don't want to reconcile automatically
+ if infisicalPushSecretCRD.Spec.ResyncInterval != nil {
logger.Error(err, fmt.Sprintf("unable to convert resync interval to duration. Will requeue after [requeueTime=%v]", requeueTime))
return ctrl.Result{
RequeueAfter: requeueTime,
}, nil
+ } else {
+ logger.Error(err, "unable to convert resync interval to duration")
+ return ctrl.Result{}, err
}
+ }
- requeueTime = duration
+ requeueTime = duration
+ if requeueTime != 0 {
logger.Info(fmt.Sprintf("Manual re-sync interval set. Interval: %v", requeueTime))
-
- } else {
- logger.Info(fmt.Sprintf("Re-sync interval set. Interval: %v", requeueTime))
}
// Check if the resource is already marked for deletion
@@ -137,10 +144,15 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
// Get modified/default config
infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client)
if err != nil {
- logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
- return ctrl.Result{
- RequeueAfter: requeueTime,
- }, nil
+ if requeueTime != 0 {
+ logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime))
+ return ctrl.Result{
+ RequeueAfter: requeueTime,
+ }, nil
+ } else {
+ logger.Error(err, "unable to fetch infisical-config")
+ return ctrl.Result{}, err
+ }
}
if infisicalPushSecretCRD.Spec.HostAPI == "" {
@@ -152,10 +164,15 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
if infisicalPushSecretCRD.Spec.TLS.CaRef.SecretName != "" {
api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalPushSecretCRD)
if err != nil {
- logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
- return ctrl.Result{
- RequeueAfter: requeueTime,
- }, nil
+ if requeueTime != 0 {
+ logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime))
+ return ctrl.Result{
+ RequeueAfter: requeueTime,
+ }, nil
+ } else {
+ logger.Error(err, "unable to fetch CA certificate")
+ return ctrl.Result{}, err
+ }
}
logger.Info("Using custom CA certificate...")
@@ -167,17 +184,27 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
r.SetReconcileStatusCondition(ctx, &infisicalPushSecretCRD, err)
if err != nil {
- logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Push Secret. Will requeue after [requeueTime=%v]", requeueTime))
- return ctrl.Result{
- RequeueAfter: requeueTime,
- }, nil
+ if requeueTime != 0 {
+ logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Push Secret. Will requeue after [requeueTime=%v]", requeueTime))
+ return ctrl.Result{
+ RequeueAfter: requeueTime,
+ }, nil
+ } else {
+ logger.Error(err, "unable to reconcile Infisical Push Secret")
+ return ctrl.Result{}, err
+ }
}
// Sync again after the specified time
- logger.Info(fmt.Sprintf("Operator will requeue after [%v]", requeueTime))
- return ctrl.Result{
- RequeueAfter: requeueTime,
- }, nil
+ if requeueTime != 0 {
+ logger.Info(fmt.Sprintf("Operator will requeue after [%v]", requeueTime))
+ return ctrl.Result{
+ RequeueAfter: requeueTime,
+ }, nil
+ } else {
+ logger.Info("Operator will reconcile on next spec change")
+ return ctrl.Result{}, nil
+ }
}
func (r *InfisicalPushSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
@@ -228,27 +255,67 @@ func (r *InfisicalPushSecretReconciler) SetupWithManager(mgr ctrl.Manager) error
)).
Watches(
&source.Kind{Type: &corev1.Secret{}},
- handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request {
- ctx := context.Background()
- pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{}
- if err := r.List(ctx, pushSecrets); err != nil {
- return []reconcile.Request{}
- }
-
- requests := []reconcile.Request{}
- for _, pushSecret := range pushSecrets.Items {
- if pushSecret.Spec.Push.Secret.SecretName == o.GetName() &&
- pushSecret.Spec.Push.Secret.SecretNamespace == o.GetNamespace() {
- requests = append(requests, reconcile.Request{
- NamespacedName: types.NamespacedName{
- Name: pushSecret.GetName(),
- Namespace: pushSecret.GetNamespace(),
- },
- })
- }
- }
- return requests
- }),
+ handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForSecret),
+ ).
+ Watches(
+ &source.Kind{Type: &secretsv1alpha1.ClusterGenerator{}},
+ handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForClusterGenerator),
).
Complete(r)
}
+
+func (r *InfisicalPushSecretReconciler) findPushSecretsForClusterGenerator(o client.Object) []reconcile.Request {
+ ctx := context.Background()
+ pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{}
+ if err := r.List(ctx, pushSecrets); err != nil {
+ return []reconcile.Request{}
+ }
+
+ clusterGenerator, ok := o.(*secretsv1alpha1.ClusterGenerator)
+ if !ok {
+ return []reconcile.Request{}
+ }
+
+ requests := []reconcile.Request{}
+ for _, pushSecret := range pushSecrets.Items {
+ if pushSecret.Spec.Push.Generators != nil {
+ for _, generator := range pushSecret.Spec.Push.Generators {
+ if generator.GeneratorRef.Name == clusterGenerator.GetName() {
+ requests = append(requests, reconcile.Request{
+ NamespacedName: types.NamespacedName{
+ Name: pushSecret.GetName(),
+ Namespace: pushSecret.GetNamespace(),
+ },
+ })
+ break
+ }
+ }
+ }
+ }
+ return requests
+}
+
+func (r *InfisicalPushSecretReconciler) findPushSecretsForSecret(o client.Object) []reconcile.Request {
+ ctx := context.Background()
+ pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{}
+ if err := r.List(ctx, pushSecrets); err != nil {
+ return []reconcile.Request{}
+ }
+
+ requests := []reconcile.Request{}
+ for _, pushSecret := range pushSecrets.Items {
+ if pushSecret.Spec.Push.Secret != nil &&
+ pushSecret.Spec.Push.Secret.SecretName == o.GetName() &&
+ pushSecret.Spec.Push.Secret.SecretNamespace == o.GetNamespace() {
+ requests = append(requests, reconcile.Request{
+ NamespacedName: types.NamespacedName{
+ Name: pushSecret.GetName(),
+ Namespace: pushSecret.GetNamespace(),
+ },
+ })
+ }
+
+ }
+
+ return requests
+}
diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
index 848df3c82..1400b163b 100644
--- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
+++ b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
@@ -19,6 +19,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
+ generatorUtil "github.com/Infisical/infisical/k8-operator/packages/generator"
infisicalSdk "github.com/infisical/go-sdk"
k8Errors "k8s.io/apimachinery/pkg/api/errors"
)
@@ -106,6 +107,52 @@ func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSec
infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables
}
+func (r *InfisicalPushSecretReconciler) processGenerators(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) (map[string]string, error) {
+
+ processedSecrets := make(map[string]string)
+
+ if len(infisicalPushSecret.Spec.Push.Generators) == 0 {
+ return processedSecrets, nil
+ }
+
+ for _, generator := range infisicalPushSecret.Spec.Push.Generators {
+ generatorRef := generator.GeneratorRef
+
+ clusterGenerator := &v1alpha1.ClusterGenerator{}
+ err := r.Client.Get(ctx, types.NamespacedName{Name: generatorRef.Name}, clusterGenerator)
+ if err != nil {
+ return nil, fmt.Errorf("unable to get ClusterGenerator resource [err=%s]", err)
+ }
+ if generatorRef.Kind == v1alpha1.GeneratorKindPassword {
+ // get the custom ClusterGenerator resource from the cluster
+
+ if clusterGenerator.Spec.Generator.PasswordSpec == nil {
+ return nil, fmt.Errorf("password spec is not defined in the ClusterGenerator resource")
+ }
+
+ password, err := generatorUtil.GeneratorPassword(*clusterGenerator.Spec.Generator.PasswordSpec)
+ if err != nil {
+ return nil, fmt.Errorf("unable to generate password [err=%s]", err)
+ }
+
+ processedSecrets[generator.DestinationSecretName] = password
+ }
+
+ if generatorRef.Kind == v1alpha1.GeneratorKindUUID {
+
+ uuid, err := generatorUtil.GeneratorUUID()
+ if err != nil {
+ return nil, fmt.Errorf("unable to generate UUID [err=%s]", err)
+ }
+
+ processedSecrets[generator.DestinationSecretName] = uuid
+ }
+ }
+
+ return processedSecrets, nil
+
+}
+
func (r *InfisicalPushSecretReconciler) processTemplatedSecrets(infisicalPushSecret v1alpha1.InfisicalPushSecret, kubePushSecret *corev1.Secret, destination v1alpha1.InfisicalPushSecretDestination) (map[string]string, error) {
processedSecrets := make(map[string]string)
@@ -172,18 +219,31 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
})
}
- kubePushSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
- Namespace: infisicalPushSecret.Spec.Push.Secret.SecretNamespace,
- Name: infisicalPushSecret.Spec.Push.Secret.SecretName,
- })
+ processedSecrets := make(map[string]string)
- if err != nil {
- return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
+ if infisicalPushSecret.Spec.Push.Secret != nil {
+ kubePushSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
+ Namespace: infisicalPushSecret.Spec.Push.Secret.SecretNamespace,
+ Name: infisicalPushSecret.Spec.Push.Secret.SecretName,
+ })
+
+ if err != nil {
+ return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
+ }
+
+ processedSecrets, err = r.processTemplatedSecrets(infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination)
+ if err != nil {
+ return fmt.Errorf("unable to process templated secrets [err=%s]", err)
+ }
}
- processedSecrets, err := r.processTemplatedSecrets(infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination)
+ generatorSecrets, err := r.processGenerators(ctx, infisicalPushSecret)
if err != nil {
- return fmt.Errorf("unable to process templated secrets [err=%s]", err)
+ return fmt.Errorf("unable to process generators [err=%s]", err)
+ }
+
+ for key, value := range generatorSecrets {
+ processedSecrets[key] = value
}
destination := infisicalPushSecret.Spec.Destination
diff --git a/k8-operator/go.mod b/k8-operator/go.mod
index 6ce68ed7f..c9b868b00 100644
--- a/k8-operator/go.mod
+++ b/k8-operator/go.mod
@@ -4,10 +4,12 @@ go 1.21
require (
github.com/Masterminds/sprig/v3 v3.3.0
+ github.com/aws/smithy-go v1.20.3
github.com/infisical/go-sdk v0.4.4
github.com/lestrrat-go/jwx/v2 v2.1.4
github.com/onsi/ginkgo/v2 v2.6.0
github.com/onsi/gomega v1.24.1
+ github.com/sethvargo/go-password v0.3.1
k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1
sigs.k8s.io/controller-runtime v0.14.4
@@ -34,7 +36,6 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
- github.com/aws/smithy-go v1.20.3 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -85,7 +86,7 @@ require (
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
- github.com/google/uuid v1.6.0 // indirect
+ github.com/google/uuid v1.6.0
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
diff --git a/k8-operator/go.sum b/k8-operator/go.sum
index bcecfadc0..2e151b0a4 100644
--- a/k8-operator/go.sum
+++ b/k8-operator/go.sum
@@ -338,6 +338,8 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
+github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
+github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
diff --git a/k8-operator/packages/generator/generator.go b/k8-operator/packages/generator/generator.go
new file mode 100644
index 000000000..cc1b290c7
--- /dev/null
+++ b/k8-operator/packages/generator/generator.go
@@ -0,0 +1 @@
+package generator
diff --git a/k8-operator/packages/generator/password.go b/k8-operator/packages/generator/password.go
new file mode 100644
index 000000000..d322f1014
--- /dev/null
+++ b/k8-operator/packages/generator/password.go
@@ -0,0 +1,76 @@
+package generator
+
+import (
+ "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
+ "github.com/sethvargo/go-password/password"
+)
+
+const (
+ defaultLength = 24
+ defaultSymbolChars = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
+ digitFactor = 0.25
+ symbolFactor = 0.25
+)
+
+func generateSafePassword(
+ passLen int,
+ symbols int,
+ symbolCharacters string,
+ digits int,
+ noUpper bool,
+ allowRepeat bool,
+) (string, error) {
+ gen, err := password.NewGenerator(&password.GeneratorInput{
+ Symbols: symbolCharacters,
+ })
+ if err != nil {
+ return "", err
+ }
+ return gen.Generate(
+ passLen,
+ digits,
+ symbols,
+ noUpper,
+ allowRepeat,
+ )
+}
+
+func GeneratorPassword(spec v1alpha1.PasswordSpec) (string, error) {
+
+ symbolCharacters := defaultSymbolChars
+
+ if spec.SymbolCharacters != nil && *spec.SymbolCharacters != "" {
+ symbolCharacters = *spec.SymbolCharacters
+ }
+
+ passwordLength := defaultLength
+
+ if spec.Length != 0 {
+ passwordLength = spec.Length
+ }
+
+ digits := int(float32(passwordLength) * digitFactor)
+ if spec.Digits != nil {
+ digits = *spec.Digits
+ }
+
+ symbols := int(float32(passwordLength) * symbolFactor)
+ if spec.Symbols != nil {
+ symbols = *spec.Symbols
+ }
+
+ pass, err := generateSafePassword(
+ passwordLength,
+ symbols,
+ symbolCharacters,
+ digits,
+ spec.NoUpper,
+ spec.AllowRepeat,
+ )
+
+ if err != nil {
+ return "", err
+ }
+
+ return pass, nil
+}
diff --git a/k8-operator/packages/generator/uuid.go b/k8-operator/packages/generator/uuid.go
new file mode 100644
index 000000000..b9249f783
--- /dev/null
+++ b/k8-operator/packages/generator/uuid.go
@@ -0,0 +1,10 @@
+package generator
+
+import (
+ "github.com/google/uuid"
+)
+
+func GeneratorUUID() (string, error) {
+ uuid := uuid.New().String()
+ return uuid, nil
+}
diff --git a/k8-operator/packages/util/helpers.go b/k8-operator/packages/util/helpers.go
index 02621dcfd..ef3712715 100644
--- a/k8-operator/packages/util/helpers.go
+++ b/k8-operator/packages/util/helpers.go
@@ -7,14 +7,19 @@ import (
"time"
)
-func ConvertIntervalToDuration(resyncInterval string) (time.Duration, error) {
- length := len(resyncInterval)
+func ConvertIntervalToDuration(resyncInterval *string) (time.Duration, error) {
+
+ if resyncInterval == nil || *resyncInterval == "" {
+ return 0, nil
+ }
+
+ length := len(*resyncInterval)
if length < 2 {
return 0, fmt.Errorf("invalid format")
}
- unit := resyncInterval[length-1:]
- numberPart := resyncInterval[:length-1]
+ unit := (*resyncInterval)[length-1:]
+ numberPart := (*resyncInterval)[:length-1]
number, err := strconv.Atoi(numberPart)
if err != nil {
@@ -40,16 +45,6 @@ func ConvertIntervalToDuration(resyncInterval string) (time.Duration, error) {
}
}
-func ConvertIntervalToTime(resyncInterval string) (time.Time, error) {
- duration, err := ConvertIntervalToDuration(resyncInterval)
- if err != nil {
- return time.Time{}, err
- }
-
- // Add duration to current time
- return time.Now().Add(duration), nil
-}
-
func AppendAPIEndpoint(address string) string {
if strings.HasSuffix(address, "/api") {
return address