mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #3540 from Infisical/daniel/generators
feat(k8s): generator support
This commit is contained in:
@@ -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
|
||||
|
||||
<Accordion title="resyncInterval">
|
||||
|
||||
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.
|
||||
</Accordion>
|
||||
|
||||
## 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:
|
||||
|
||||
<Accordion title="push.generators[]">
|
||||
Define a generator in the `push.generators[]` field.
|
||||
|
||||
<Accordion title="push.generators[].destinationSecretName">
|
||||
The name of the secret that will be created in Infisical.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="push.generators[].generatorRef">
|
||||
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.
|
||||
</Accordion>
|
||||
</Accordion>
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Password Generator">
|
||||
|
||||
The Password generator is a custom resource that is installed on the cluster that defines the logic for generating a password.
|
||||
<Accordion title="Spec definition">
|
||||
|
||||
- `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.
|
||||
|
||||
<Accordion title="generator.passwordSpec">
|
||||
- `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.
|
||||
</Accordion>
|
||||
</Accordion>
|
||||
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="UUID Generator">
|
||||
The UUID generator is a custom resource that is installed on the cluster that defines the logic for generating a UUID.
|
||||
<Accordion title="Spec definition">
|
||||
- `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.
|
||||
</Accordion>
|
||||
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
152
k8-operator/api/v1alpha1/generators.go
Normal file
152
k8-operator/api/v1alpha1/generators.go
Normal file
@@ -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{})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: {}
|
||||
@@ -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
|
||||
|
||||
@@ -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: {}
|
||||
@@ -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: {}
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
1
k8-operator/packages/generator/generator.go
Normal file
1
k8-operator/packages/generator/generator.go
Normal file
@@ -0,0 +1 @@
|
||||
package generator
|
||||
76
k8-operator/packages/generator/password.go
Normal file
76
k8-operator/packages/generator/password.go
Normal 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 && *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
|
||||
}
|
||||
10
k8-operator/packages/generator/uuid.go
Normal file
10
k8-operator/packages/generator/uuid.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func GeneratorUUID() (string, error) {
|
||||
uuid := uuid.New().String()
|
||||
return uuid, nil
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user