feat(k8s): generators

This commit is contained in:
Daniel Hougaard
2025-05-05 00:59:11 +04:00
parent 219964a242
commit 9f7599b2a1
16 changed files with 989 additions and 75 deletions

View File

@@ -0,0 +1,150 @@
/*
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.
// +kubebuilder:validation:Optional
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 1 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: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:default=false
NoUpper bool `json:"noUpper"`
// set AllowRepeat to true to allow repeating characters.
// +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{})
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,94 @@
---
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
required:
- allowRepeat
- length
- noUpper
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: {}

View File

@@ -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

View File

@@ -0,0 +1,73 @@
---
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
required:
- allowRepeat
- length
- noUpper
type: object
type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -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 1 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: {}

View File

@@ -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)
}

View File

@@ -108,23 +108,25 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
return ctrl.Result{}, nil
}
if infisicalPushSecretCRD.Spec.ResyncInterval != "" {
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 +139,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 +159,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 +179,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 +250,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
}

View File

@@ -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,50 @@ func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSec
infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables
}
func (r *InfisicalPushSecretReconciler) processGenerators(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(context.TODO(), 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
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 {
fmt.Printf("clusterGenerator: %+v\n", clusterGenerator)
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 +217,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(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

View File

@@ -51,6 +51,7 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sethvargo/go-password v0.3.1 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
go.opencensus.io v0.24.0 // indirect

View File

@@ -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=

View File

@@ -0,0 +1 @@
package generator

View File

@@ -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 {
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
}

View File

@@ -0,0 +1,10 @@
package generator
import (
"github.com/google/uuid"
)
func GeneratorUUID() (string, error) {
uuid := uuid.New().String()
return uuid, nil
}

View File

@@ -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 {
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