mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 23:28:25 +00:00
feat(k8-operator): dynamic secrets
This commit is contained in:
@@ -26,4 +26,13 @@ resources:
|
|||||||
kind: InfisicalPushSecretSecret
|
kind: InfisicalPushSecretSecret
|
||||||
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||||
version: v1alpha1
|
version: v1alpha1
|
||||||
|
- api:
|
||||||
|
crdVersion: v1
|
||||||
|
namespaced: true
|
||||||
|
controller: true
|
||||||
|
domain: infisical.com
|
||||||
|
group: secrets
|
||||||
|
kind: InfisicalDynamicSecret
|
||||||
|
path: github.com/Infisical/infisical/k8-operator/api/v1alpha1
|
||||||
|
version: v1alpha1
|
||||||
version: "3"
|
version: "3"
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package v1alpha1
|
||||||
|
|
||||||
|
type GenericInfisicalAuthentication struct {
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
UniversalAuth GenericUniversalAuth `json:"universalAuth,omitempty"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
KubernetesAuth GenericKubernetesAuth `json:"kubernetesAuth,omitempty"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
AwsIamAuth GenericAwsIamAuth `json:"awsIamAuth,omitempty"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
AzureAuth GenericAzureAuth `json:"azureAuth,omitempty"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericUniversalAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericAwsIamAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
IdentityID string `json:"identityId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericAzureAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
IdentityID string `json:"identityId"`
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericGcpIdTokenAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
IdentityID string `json:"identityId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericGcpIamAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
IdentityID string `json:"identityId"`
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenericKubernetesAuth struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
IdentityID string `json:"identityId"`
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSConfig struct {
|
||||||
|
// Reference to secret containing CA cert
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
CaRef CaReference `json:"caRef,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CaReference struct {
|
||||||
|
// The name of the Kubernetes Secret
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretName string `json:"secretName"`
|
||||||
|
|
||||||
|
// The namespace where the Kubernetes Secret is located
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretNamespace string `json:"secretNamespace"`
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// The name of the secret property with the CA certificate value
|
||||||
|
SecretKey string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubeSecretReference struct {
|
||||||
|
// The name of the Kubernetes Secret
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretName string `json:"secretName"`
|
||||||
|
|
||||||
|
// The name space where the Kubernetes Secret is located
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretNamespace string `json:"secretNamespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManagedKubeSecretConfig struct {
|
||||||
|
// The name of the Kubernetes Secret
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretName string `json:"secretName"`
|
||||||
|
|
||||||
|
// The name space where the Kubernetes Secret is located
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
SecretNamespace string `json:"secretNamespace"`
|
||||||
|
|
||||||
|
// The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
// +kubebuilder:default:=Opaque
|
||||||
|
SecretType string `json:"secretType"`
|
||||||
|
|
||||||
|
// The Kubernetes Secret creation policy.
|
||||||
|
// Enum with values: 'Owner', 'Orphan'.
|
||||||
|
// Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
||||||
|
// Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
// +kubebuilder:default:=Orphan
|
||||||
|
CreationPolicy string `json:"creationPolicy"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InfisicalDynamicSecretLease struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Version int64 `json:"version"`
|
||||||
|
CreationTimestamp metav1.Time `json:"creationTimestamp"`
|
||||||
|
ExpiresAt metav1.Time `json:"expiresAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicSecretDetails struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +kubebuilder:validation:Immutable
|
||||||
|
SecretName string `json:"secretName"`
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +kubebuilder:validation:Immutable
|
||||||
|
SecretPath string `json:"secretsPath"`
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +kubebuilder:validation:Immutable
|
||||||
|
EnvironmentSlug string `json:"environmentSlug"`
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
// +kubebuilder:validation:Immutable
|
||||||
|
ProjectID string `json:"projectId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||||
|
type InfisicalDynamicSecretSpec struct {
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` // The destination to store the lease in.
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
Authentication GenericInfisicalAuthentication `json:"authentication"` // The authentication to use for authenticating with Infisical.
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Required
|
||||||
|
DynamicSecret DynamicSecretDetails `json:"dynamicSecret"` // The dynamic secret to create the lease for. Required.
|
||||||
|
|
||||||
|
LeaseRevocationPolicy string `json:"leaseRevocationPolicy"` // Revoke will revoke the lease when the resource is deleted. Optional, will default to no revocation.
|
||||||
|
LeaseTTL string `json:"leaseTTL"` // The TTL of the lease in seconds. Optional, will default to the dynamic secret default TTL.
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
HostAPI string `json:"hostAPI"`
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
TLS TLSConfig `json:"tls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret.
|
||||||
|
type InfisicalDynamicSecretStatus struct {
|
||||||
|
Lease *InfisicalDynamicSecretLease `json:"lease,omitempty"`
|
||||||
|
|
||||||
|
DynamicSecretID string `json:"dynamicSecretId,omitempty"`
|
||||||
|
|
||||||
|
// The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew.
|
||||||
|
MaxTTL string `json:"maxTTL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
// +kubebuilder:subresource:status
|
||||||
|
|
||||||
|
// InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets API.
|
||||||
|
type InfisicalDynamicSecret struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||||
|
|
||||||
|
Spec InfisicalDynamicSecretSpec `json:"spec,omitempty"`
|
||||||
|
Status InfisicalDynamicSecretStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:object:root=true
|
||||||
|
|
||||||
|
// InfisicalDynamicSecretList contains a list of InfisicalDynamicSecret.
|
||||||
|
type InfisicalDynamicSecretList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata,omitempty"`
|
||||||
|
Items []InfisicalDynamicSecret `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SchemeBuilder.Register(&InfisicalDynamicSecret{}, &InfisicalDynamicSecretList{})
|
||||||
|
}
|
||||||
@@ -16,64 +16,6 @@ type InfisicalPushSecretDestination struct {
|
|||||||
ProjectID string `json:"projectId"`
|
ProjectID string `json:"projectId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PushSecretTlsConfig struct {
|
|
||||||
// Reference to secret containing CA cert
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
CaRef CaReference `json:"caRef,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushSecretUniversalAuth defines universal authentication
|
|
||||||
type PushSecretUniversalAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
CredentialsRef KubeSecretReference `json:"credentialsRef"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretAwsIamAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
IdentityID string `json:"identityId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretAzureAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
IdentityID string `json:"identityId"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretGcpIdTokenAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
IdentityID string `json:"identityId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretGcpIamAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
IdentityID string `json:"identityId"`
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretKubernetesAuth struct {
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
IdentityID string `json:"identityId"`
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PushSecretAuthentication struct {
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
UniversalAuth PushSecretUniversalAuth `json:"universalAuth,omitempty"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
KubernetesAuth PushSecretKubernetesAuth `json:"kubernetesAuth,omitempty"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
AwsIamAuth PushSecretAwsIamAuth `json:"awsIamAuth,omitempty"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
AzureAuth PushSecretAzureAuth `json:"azureAuth,omitempty"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
GcpIdTokenAuth PushSecretGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"`
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
GcpIamAuth PushSecretGcpIamAuth `json:"gcpIamAuth,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SecretPush struct {
|
type SecretPush struct {
|
||||||
// +kubebuilder:validation:Required
|
// +kubebuilder:validation:Required
|
||||||
Secret KubeSecretReference `json:"secret"`
|
Secret KubeSecretReference `json:"secret"`
|
||||||
@@ -92,7 +34,7 @@ type InfisicalPushSecretSpec struct {
|
|||||||
Destination InfisicalPushSecretDestination `json:"destination"`
|
Destination InfisicalPushSecretDestination `json:"destination"`
|
||||||
|
|
||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
Authentication PushSecretAuthentication `json:"authentication"`
|
Authentication GenericInfisicalAuthentication `json:"authentication"`
|
||||||
|
|
||||||
// +kubebuilder:validation:Required
|
// +kubebuilder:validation:Required
|
||||||
Push SecretPush `json:"push"`
|
Push SecretPush `json:"push"`
|
||||||
@@ -104,7 +46,7 @@ type InfisicalPushSecretSpec struct {
|
|||||||
HostAPI string `json:"hostAPI"`
|
HostAPI string `json:"hostAPI"`
|
||||||
|
|
||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
TLS PushSecretTlsConfig `json:"tls"`
|
TLS TLSConfig `json:"tls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
|
// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret
|
||||||
|
|||||||
@@ -116,43 +116,6 @@ type MachineIdentityScopeInWorkspace struct {
|
|||||||
Recursive bool `json:"recursive"`
|
Recursive bool `json:"recursive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type KubeSecretReference struct {
|
|
||||||
// The name of the Kubernetes Secret
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretName string `json:"secretName"`
|
|
||||||
|
|
||||||
// The name space where the Kubernetes Secret is located
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretNamespace string `json:"secretNamespace"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type MangedKubeSecretConfig struct {
|
|
||||||
// The name of the Kubernetes Secret
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretName string `json:"secretName"`
|
|
||||||
|
|
||||||
// The name space where the Kubernetes Secret is located
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretNamespace string `json:"secretNamespace"`
|
|
||||||
|
|
||||||
// The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
// +kubebuilder:default:=Opaque
|
|
||||||
SecretType string `json:"secretType"`
|
|
||||||
|
|
||||||
// The Kubernetes Secret creation policy.
|
|
||||||
// Enum with values: 'Owner', 'Orphan'.
|
|
||||||
// Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it.
|
|
||||||
// Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
// +kubebuilder:default:=Orphan
|
|
||||||
CreationPolicy string `json:"creationPolicy"`
|
|
||||||
|
|
||||||
// The template to transform the secret data
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
Template *InfisicalSecretTemplate `json:"template,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type InfisicalSecretTemplate struct {
|
type InfisicalSecretTemplate struct {
|
||||||
// This injects all retrieved secrets into the top level of your template.
|
// This injects all retrieved secrets into the top level of your template.
|
||||||
// Secrets defined in the template will take precedence over the injected ones.
|
// Secrets defined in the template will take precedence over the injected ones.
|
||||||
@@ -163,26 +126,6 @@ type InfisicalSecretTemplate struct {
|
|||||||
Data map[string]string `json:"data,omitempty"`
|
Data map[string]string `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CaReference struct {
|
|
||||||
// The name of the Kubernetes Secret
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretName string `json:"secretName"`
|
|
||||||
|
|
||||||
// The namespace where the Kubernetes Secret is located
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
SecretNamespace string `json:"secretNamespace"`
|
|
||||||
|
|
||||||
// +kubebuilder:validation:Required
|
|
||||||
// The name of the secret property with the CA certificate value
|
|
||||||
SecretKey string `json:"key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSConfig struct {
|
|
||||||
// Reference to secret containing CA cert
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
CaRef CaReference `json:"caRef,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfisicalSecretSpec defines the desired state of InfisicalSecret
|
// InfisicalSecretSpec defines the desired state of InfisicalSecret
|
||||||
type InfisicalSecretSpec struct {
|
type InfisicalSecretSpec struct {
|
||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
@@ -192,7 +135,7 @@ type InfisicalSecretSpec struct {
|
|||||||
Authentication Authentication `json:"authentication"`
|
Authentication Authentication `json:"authentication"`
|
||||||
|
|
||||||
// +kubebuilder:validation:Required
|
// +kubebuilder:validation:Required
|
||||||
ManagedSecretReference MangedKubeSecretConfig `json:"managedSecretReference"`
|
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"`
|
||||||
|
|
||||||
// +kubebuilder:default:=60
|
// +kubebuilder:default:=60
|
||||||
ResyncInterval int `json:"resyncInterval"`
|
ResyncInterval int `json:"resyncInterval"`
|
||||||
|
|||||||
@@ -96,6 +96,21 @@ func (in *CaReference) DeepCopy() *CaReference {
|
|||||||
return 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DynamicSecretDetails.
|
||||||
|
func (in *DynamicSecretDetails) DeepCopy() *DynamicSecretDetails {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(DynamicSecretDetails)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *GCPIdTokenAuthDetails) DeepCopyInto(out *GCPIdTokenAuthDetails) {
|
func (in *GCPIdTokenAuthDetails) DeepCopyInto(out *GCPIdTokenAuthDetails) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -128,6 +143,234 @@ func (in *GcpIamAuthDetails) DeepCopy() *GcpIamAuthDetails {
|
|||||||
return 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericAwsIamAuth.
|
||||||
|
func (in *GenericAwsIamAuth) DeepCopy() *GenericAwsIamAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericAwsIamAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericAzureAuth) DeepCopyInto(out *GenericAzureAuth) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericAzureAuth.
|
||||||
|
func (in *GenericAzureAuth) DeepCopy() *GenericAzureAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericAzureAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericGcpIamAuth) DeepCopyInto(out *GenericGcpIamAuth) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericGcpIamAuth.
|
||||||
|
func (in *GenericGcpIamAuth) DeepCopy() *GenericGcpIamAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericGcpIamAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericGcpIdTokenAuth) DeepCopyInto(out *GenericGcpIdTokenAuth) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericGcpIdTokenAuth.
|
||||||
|
func (in *GenericGcpIdTokenAuth) DeepCopy() *GenericGcpIdTokenAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericGcpIdTokenAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericInfisicalAuthentication) DeepCopyInto(out *GenericInfisicalAuthentication) {
|
||||||
|
*out = *in
|
||||||
|
out.UniversalAuth = in.UniversalAuth
|
||||||
|
out.KubernetesAuth = in.KubernetesAuth
|
||||||
|
out.AwsIamAuth = in.AwsIamAuth
|
||||||
|
out.AzureAuth = in.AzureAuth
|
||||||
|
out.GcpIdTokenAuth = in.GcpIdTokenAuth
|
||||||
|
out.GcpIamAuth = in.GcpIamAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericInfisicalAuthentication.
|
||||||
|
func (in *GenericInfisicalAuthentication) DeepCopy() *GenericInfisicalAuthentication {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericInfisicalAuthentication)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericKubernetesAuth) DeepCopyInto(out *GenericKubernetesAuth) {
|
||||||
|
*out = *in
|
||||||
|
out.ServiceAccountRef = in.ServiceAccountRef
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericKubernetesAuth.
|
||||||
|
func (in *GenericKubernetesAuth) DeepCopy() *GenericKubernetesAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericKubernetesAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *GenericUniversalAuth) DeepCopyInto(out *GenericUniversalAuth) {
|
||||||
|
*out = *in
|
||||||
|
out.CredentialsRef = in.CredentialsRef
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenericUniversalAuth.
|
||||||
|
func (in *GenericUniversalAuth) DeepCopy() *GenericUniversalAuth {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(GenericUniversalAuth)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InfisicalDynamicSecret) DeepCopyInto(out *InfisicalDynamicSecret) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
|
out.Spec = in.Spec
|
||||||
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecret.
|
||||||
|
func (in *InfisicalDynamicSecret) DeepCopy() *InfisicalDynamicSecret {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalDynamicSecret)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *InfisicalDynamicSecret) 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 *InfisicalDynamicSecretLease) DeepCopyInto(out *InfisicalDynamicSecretLease) {
|
||||||
|
*out = *in
|
||||||
|
in.CreationTimestamp.DeepCopyInto(&out.CreationTimestamp)
|
||||||
|
in.ExpiresAt.DeepCopyInto(&out.ExpiresAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretLease.
|
||||||
|
func (in *InfisicalDynamicSecretLease) DeepCopy() *InfisicalDynamicSecretLease {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalDynamicSecretLease)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InfisicalDynamicSecretList) DeepCopyInto(out *InfisicalDynamicSecretList) {
|
||||||
|
*out = *in
|
||||||
|
out.TypeMeta = in.TypeMeta
|
||||||
|
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||||
|
if in.Items != nil {
|
||||||
|
in, out := &in.Items, &out.Items
|
||||||
|
*out = make([]InfisicalDynamicSecret, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretList.
|
||||||
|
func (in *InfisicalDynamicSecretList) DeepCopy() *InfisicalDynamicSecretList {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalDynamicSecretList)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *InfisicalDynamicSecretList) 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 *InfisicalDynamicSecretSpec) DeepCopyInto(out *InfisicalDynamicSecretSpec) {
|
||||||
|
*out = *in
|
||||||
|
out.ManagedSecretReference = in.ManagedSecretReference
|
||||||
|
out.Authentication = in.Authentication
|
||||||
|
out.DynamicSecret = in.DynamicSecret
|
||||||
|
out.TLS = in.TLS
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretSpec.
|
||||||
|
func (in *InfisicalDynamicSecretSpec) DeepCopy() *InfisicalDynamicSecretSpec {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalDynamicSecretSpec)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InfisicalDynamicSecretStatus) DeepCopyInto(out *InfisicalDynamicSecretStatus) {
|
||||||
|
*out = *in
|
||||||
|
if in.Lease != nil {
|
||||||
|
in, out := &in.Lease, &out.Lease
|
||||||
|
*out = new(InfisicalDynamicSecretLease)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretStatus.
|
||||||
|
func (in *InfisicalDynamicSecretStatus) DeepCopy() *InfisicalDynamicSecretStatus {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalDynamicSecretStatus)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *InfisicalPushSecret) DeepCopyInto(out *InfisicalPushSecret) {
|
func (in *InfisicalPushSecret) DeepCopyInto(out *InfisicalPushSecret) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -435,7 +678,7 @@ func (in *MachineIdentityScopeInWorkspace) DeepCopy() *MachineIdentityScopeInWor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *MangedKubeSecretConfig) DeepCopyInto(out *MangedKubeSecretConfig) {
|
func (in *ManagedKubeSecretConfig) DeepCopyInto(out *ManagedKubeSecretConfig) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.Template != nil {
|
if in.Template != nil {
|
||||||
in, out := &in.Template, &out.Template
|
in, out := &in.Template, &out.Template
|
||||||
@@ -444,141 +687,12 @@ func (in *MangedKubeSecretConfig) DeepCopyInto(out *MangedKubeSecretConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MangedKubeSecretConfig.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedKubeSecretConfig.
|
||||||
func (in *MangedKubeSecretConfig) DeepCopy() *MangedKubeSecretConfig {
|
func (in *ManagedKubeSecretConfig) DeepCopy() *ManagedKubeSecretConfig {
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
out := new(MangedKubeSecretConfig)
|
out := new(ManagedKubeSecretConfig)
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretAuthentication) DeepCopyInto(out *PushSecretAuthentication) {
|
|
||||||
*out = *in
|
|
||||||
out.UniversalAuth = in.UniversalAuth
|
|
||||||
out.KubernetesAuth = in.KubernetesAuth
|
|
||||||
out.AwsIamAuth = in.AwsIamAuth
|
|
||||||
out.AzureAuth = in.AzureAuth
|
|
||||||
out.GcpIdTokenAuth = in.GcpIdTokenAuth
|
|
||||||
out.GcpIamAuth = in.GcpIamAuth
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAuthentication.
|
|
||||||
func (in *PushSecretAuthentication) DeepCopy() *PushSecretAuthentication {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretAuthentication)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretAwsIamAuth) DeepCopyInto(out *PushSecretAwsIamAuth) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAwsIamAuth.
|
|
||||||
func (in *PushSecretAwsIamAuth) DeepCopy() *PushSecretAwsIamAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretAwsIamAuth)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretAzureAuth) DeepCopyInto(out *PushSecretAzureAuth) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretAzureAuth.
|
|
||||||
func (in *PushSecretAzureAuth) DeepCopy() *PushSecretAzureAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretAzureAuth)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretGcpIamAuth) DeepCopyInto(out *PushSecretGcpIamAuth) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretGcpIamAuth.
|
|
||||||
func (in *PushSecretGcpIamAuth) DeepCopy() *PushSecretGcpIamAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretGcpIamAuth)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretGcpIdTokenAuth) DeepCopyInto(out *PushSecretGcpIdTokenAuth) {
|
|
||||||
*out = *in
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretGcpIdTokenAuth.
|
|
||||||
func (in *PushSecretGcpIdTokenAuth) DeepCopy() *PushSecretGcpIdTokenAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretGcpIdTokenAuth)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretKubernetesAuth) DeepCopyInto(out *PushSecretKubernetesAuth) {
|
|
||||||
*out = *in
|
|
||||||
out.ServiceAccountRef = in.ServiceAccountRef
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretKubernetesAuth.
|
|
||||||
func (in *PushSecretKubernetesAuth) DeepCopy() *PushSecretKubernetesAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretKubernetesAuth)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretTlsConfig) DeepCopyInto(out *PushSecretTlsConfig) {
|
|
||||||
*out = *in
|
|
||||||
out.CaRef = in.CaRef
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretTlsConfig.
|
|
||||||
func (in *PushSecretTlsConfig) DeepCopy() *PushSecretTlsConfig {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretTlsConfig)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *PushSecretUniversalAuth) DeepCopyInto(out *PushSecretUniversalAuth) {
|
|
||||||
*out = *in
|
|
||||||
out.CredentialsRef = in.CredentialsRef
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretUniversalAuth.
|
|
||||||
func (in *PushSecretUniversalAuth) DeepCopy() *PushSecretUniversalAuth {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(PushSecretUniversalAuth)
|
|
||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,222 @@
|
|||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
controller-gen.kubebuilder.io/version: v0.10.0
|
||||||
|
creationTimestamp: null
|
||||||
|
name: infisicaldynamicsecrets.secrets.infisical.com
|
||||||
|
spec:
|
||||||
|
group: secrets.infisical.com
|
||||||
|
names:
|
||||||
|
kind: InfisicalDynamicSecret
|
||||||
|
listKind: InfisicalDynamicSecretList
|
||||||
|
plural: infisicaldynamicsecrets
|
||||||
|
singular: infisicaldynamicsecret
|
||||||
|
scope: Namespaced
|
||||||
|
versions:
|
||||||
|
- name: v1alpha1
|
||||||
|
schema:
|
||||||
|
openAPIV3Schema:
|
||||||
|
description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets
|
||||||
|
API.
|
||||||
|
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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret.
|
||||||
|
properties:
|
||||||
|
authentication:
|
||||||
|
properties:
|
||||||
|
awsIamAuth:
|
||||||
|
properties:
|
||||||
|
identityId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- identityId
|
||||||
|
type: object
|
||||||
|
azureAuth:
|
||||||
|
properties:
|
||||||
|
identityId:
|
||||||
|
type: string
|
||||||
|
resource:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- identityId
|
||||||
|
type: object
|
||||||
|
gcpIamAuth:
|
||||||
|
properties:
|
||||||
|
identityId:
|
||||||
|
type: string
|
||||||
|
serviceAccountKeyFilePath:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- identityId
|
||||||
|
- serviceAccountKeyFilePath
|
||||||
|
type: object
|
||||||
|
gcpIdTokenAuth:
|
||||||
|
properties:
|
||||||
|
identityId:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- identityId
|
||||||
|
type: object
|
||||||
|
kubernetesAuth:
|
||||||
|
properties:
|
||||||
|
identityId:
|
||||||
|
type: string
|
||||||
|
serviceAccountRef:
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- namespace
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- identityId
|
||||||
|
- serviceAccountRef
|
||||||
|
type: object
|
||||||
|
universalAuth:
|
||||||
|
properties:
|
||||||
|
credentialsRef:
|
||||||
|
properties:
|
||||||
|
secretName:
|
||||||
|
description: The name of the Kubernetes Secret
|
||||||
|
type: string
|
||||||
|
secretNamespace:
|
||||||
|
description: The name space where the Kubernetes Secret
|
||||||
|
is located
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- secretName
|
||||||
|
- secretNamespace
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- credentialsRef
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
dynamicSecret:
|
||||||
|
properties:
|
||||||
|
environmentSlug:
|
||||||
|
type: string
|
||||||
|
projectId:
|
||||||
|
type: string
|
||||||
|
secretName:
|
||||||
|
type: string
|
||||||
|
secretsPath:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- environmentSlug
|
||||||
|
- projectId
|
||||||
|
- secretName
|
||||||
|
- secretsPath
|
||||||
|
type: object
|
||||||
|
hostAPI:
|
||||||
|
type: string
|
||||||
|
leaseRevocationPolicy:
|
||||||
|
type: string
|
||||||
|
leaseTTL:
|
||||||
|
type: string
|
||||||
|
managedSecretReference:
|
||||||
|
properties:
|
||||||
|
creationPolicy:
|
||||||
|
default: Orphan
|
||||||
|
description: 'The Kubernetes Secret creation policy. Enum with
|
||||||
|
values: ''Owner'', ''Orphan''. Owner creates the secret and
|
||||||
|
sets .metadata.ownerReferences of the InfisicalSecret CRD that
|
||||||
|
created it. Orphan will not set the secret owner. This will
|
||||||
|
result in the secret being orphaned and not deleted when the
|
||||||
|
resource is deleted.'
|
||||||
|
type: string
|
||||||
|
secretName:
|
||||||
|
description: The name of the Kubernetes Secret
|
||||||
|
type: string
|
||||||
|
secretNamespace:
|
||||||
|
description: The name space where the Kubernetes Secret is located
|
||||||
|
type: string
|
||||||
|
secretType:
|
||||||
|
default: Opaque
|
||||||
|
description: 'The Kubernetes Secret type (experimental feature).
|
||||||
|
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- secretName
|
||||||
|
- secretNamespace
|
||||||
|
type: object
|
||||||
|
tls:
|
||||||
|
properties:
|
||||||
|
caRef:
|
||||||
|
description: Reference to secret containing CA cert
|
||||||
|
properties:
|
||||||
|
key:
|
||||||
|
description: The name of the secret property with the CA certificate
|
||||||
|
value
|
||||||
|
type: string
|
||||||
|
secretName:
|
||||||
|
description: The name of the Kubernetes Secret
|
||||||
|
type: string
|
||||||
|
secretNamespace:
|
||||||
|
description: The namespace where the Kubernetes Secret is
|
||||||
|
located
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- key
|
||||||
|
- secretName
|
||||||
|
- secretNamespace
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- authentication
|
||||||
|
- dynamicSecret
|
||||||
|
- leaseRevocationPolicy
|
||||||
|
- leaseTTL
|
||||||
|
- managedSecretReference
|
||||||
|
type: object
|
||||||
|
status:
|
||||||
|
description: InfisicalDynamicSecretStatus defines the observed state of
|
||||||
|
InfisicalDynamicSecret.
|
||||||
|
properties:
|
||||||
|
dynamicSecretId:
|
||||||
|
type: string
|
||||||
|
lease:
|
||||||
|
properties:
|
||||||
|
creationTimestamp:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
expiresAt:
|
||||||
|
format: date-time
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
version:
|
||||||
|
format: int64
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- creationTimestamp
|
||||||
|
- expiresAt
|
||||||
|
- id
|
||||||
|
- version
|
||||||
|
type: object
|
||||||
|
maxTTL:
|
||||||
|
description: The MaxTTL can be null, if it's null, there's no max
|
||||||
|
TTL and we should never have to renew.
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
type: object
|
||||||
|
served: true
|
||||||
|
storage: true
|
||||||
|
subresources:
|
||||||
|
status: {}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
resources:
|
resources:
|
||||||
- bases/secrets.infisical.com_infisicalsecrets.yaml
|
- bases/secrets.infisical.com_infisicalsecrets.yaml
|
||||||
- bases/secrets.infisical.com_infisicalpushsecrets.yaml
|
- bases/secrets.infisical.com_infisicalpushsecrets.yaml
|
||||||
|
- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml
|
||||||
#+kubebuilder:scaffold:crdkustomizeresource
|
#+kubebuilder:scaffold:crdkustomizeresource
|
||||||
|
|
||||||
patchesStrategicMerge:
|
patchesStrategicMerge:
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# permissions for end users to edit infisicaldynamicsecrets.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: k8-operator
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: infisicaldynamicsecret-editor-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# permissions for end users to view infisicaldynamicsecrets.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: k8-operator
|
||||||
|
app.kubernetes.io/managed-by: kustomize
|
||||||
|
name: infisicaldynamicsecret-viewer-role
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
@@ -44,6 +44,32 @@ rules:
|
|||||||
- list
|
- list
|
||||||
- update
|
- update
|
||||||
- watch
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets/finalizers
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
- apiGroups:
|
||||||
|
- secrets.infisical.com
|
||||||
|
resources:
|
||||||
|
- infisicaldynamicsecrets/status
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- secrets.infisical.com
|
- secrets.infisical.com
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
apiVersion: secrets.infisical.com/v1alpha1
|
apiVersion: secrets.infisical.com/v1alpha1
|
||||||
kind: InfisicalPushSecret
|
kind: InfisicalPushSecret
|
||||||
metadata:
|
metadata:
|
||||||
name: infisical-push-secret-demo
|
name: infisical-api-secret-sample-push
|
||||||
spec:
|
spec:
|
||||||
resyncInterval: 1m
|
resyncInterval: 1m
|
||||||
hostAPI: https://app.infisical.com/api
|
hostAPI: https://app.infisical.com/api
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||||
|
|
||||||
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||||
|
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
|
||||||
|
type InfisicalDynamicSecretReconciler struct {
|
||||||
|
client.Client
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
|
||||||
|
BaseLogger logr.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update
|
||||||
|
|
||||||
|
var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
||||||
|
return r.BaseLogger.WithValues("infisicaldynamicsecret", req.NamespacedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
|
||||||
|
logger := r.GetLogger(req)
|
||||||
|
|
||||||
|
var infisicalDynamicSecretCRD secretsv1alpha1.InfisicalDynamicSecret
|
||||||
|
requeueTime := time.Second * 5
|
||||||
|
|
||||||
|
err := r.Get(ctx, req.NamespacedName, &infisicalDynamicSecretCRD)
|
||||||
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
logger.Info("Infisical Dynamic Secret CRD not found")
|
||||||
|
return ctrl.Result{
|
||||||
|
Requeue: false,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
logger.Error(err, "Unable to fetch Infisical Dynamic Secret CRD from cluster")
|
||||||
|
return ctrl.Result{
|
||||||
|
RequeueAfter: requeueTime,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add finalizer if it doesn't exist
|
||||||
|
if !controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||||
|
controllerutil.AddFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME)
|
||||||
|
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's being deleted
|
||||||
|
if !infisicalDynamicSecretCRD.DeletionTimestamp.IsZero() {
|
||||||
|
logger.Info("Handling deletion of InfisicalDynamicSecret")
|
||||||
|
if controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) {
|
||||||
|
// We remove finalizers before running deletion logic to be completely safe from stuck resources
|
||||||
|
infisicalDynamicSecretCRD.ObjectMeta.Finalizers = []string{}
|
||||||
|
if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil {
|
||||||
|
logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalDynamicSecret %s", infisicalDynamicSecretCRD.Name))
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecretCRD)
|
||||||
|
|
||||||
|
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||||
|
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecretCRD.GetUID())]; ok {
|
||||||
|
rv.CancelCtx()
|
||||||
|
delete(infisicalDynamicSecretsResourceVariablesMap, string(infisicalDynamicSecretCRD.GetUID()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 infisicalDynamicSecretCRD.Spec.HostAPI == "" {
|
||||||
|
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||||
|
} else {
|
||||||
|
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalDynamicSecretCRD.Spec.HostAPI)
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalDynamicSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||||
|
api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalDynamicSecretCRD)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("Using custom CA certificate...")
|
||||||
|
} else {
|
||||||
|
api.API_CA_CERTIFICATE = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
nextReconcile, err := r.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecretCRD)
|
||||||
|
// r.SetSuccessfullyReconciledConditions(ctx, &infisicalDynamicSecretCRD, err)
|
||||||
|
|
||||||
|
if err == nil && nextReconcile.Seconds() >= 5 {
|
||||||
|
requeueTime = nextReconcile
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||||
|
return ctrl.Result{
|
||||||
|
RequeueAfter: requeueTime,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync again after the specified time
|
||||||
|
logger.Info(fmt.Sprintf("Next reconciliation in [requeueTime=%v]", requeueTime))
|
||||||
|
return ctrl.Result{
|
||||||
|
RequeueAfter: requeueTime,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
|
||||||
|
// Custom predicate that allows both spec changes and deletions
|
||||||
|
specChangeOrDelete := predicate.Funcs{
|
||||||
|
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||||
|
// Only reconcile if spec/generation changed
|
||||||
|
|
||||||
|
isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration()
|
||||||
|
|
||||||
|
if isSpecOrGenerationChange {
|
||||||
|
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||||
|
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok {
|
||||||
|
rv.CancelCtx()
|
||||||
|
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.ObjectNew.GetUID()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSpecOrGenerationChange
|
||||||
|
},
|
||||||
|
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||||
|
// Always reconcile on deletion
|
||||||
|
|
||||||
|
if infisicalDynamicSecretsResourceVariablesMap != nil {
|
||||||
|
if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.Object.GetUID())]; ok {
|
||||||
|
rv.CancelCtx()
|
||||||
|
delete(infisicalDynamicSecretsResourceVariablesMap, string(e.Object.GetUID()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
CreateFunc: func(e event.CreateEvent) bool {
|
||||||
|
// Reconcile on creation
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
GenericFunc: func(e event.GenericEvent) bool {
|
||||||
|
// Ignore generic events
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&secretsv1alpha1.InfisicalDynamicSecret{}, builder.WithPredicates(
|
||||||
|
specChangeOrDelete,
|
||||||
|
)).
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
@@ -0,0 +1,446 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
|
infisicalSdk "github.com/infisical/go-sdk"
|
||||||
|
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error {
|
||||||
|
secretType := infisicalDynamicSecret.Spec.ManagedSecretReference.SecretType
|
||||||
|
|
||||||
|
// copy labels and annotations from InfisicalSecret CRD
|
||||||
|
labels := map[string]string{}
|
||||||
|
for k, v := range infisicalDynamicSecret.Labels {
|
||||||
|
labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations := map[string]string{}
|
||||||
|
systemPrefixes := []string{"kubectl.kubernetes.io/", "kubernetes.io/", "k8s.io/", "helm.sh/"}
|
||||||
|
for k, v := range infisicalDynamicSecret.Annotations {
|
||||||
|
isSystem := false
|
||||||
|
for _, prefix := range systemPrefixes {
|
||||||
|
if strings.HasPrefix(k, prefix) {
|
||||||
|
isSystem = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isSystem {
|
||||||
|
annotations[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
annotations[constants.SECRET_VERSION_ANNOTATION] = versionAnnotationValue
|
||||||
|
|
||||||
|
// create a new secret as specified by the managed secret spec of CRD
|
||||||
|
newKubeSecretInstance := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||||
|
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||||
|
Annotations: annotations,
|
||||||
|
Labels: labels,
|
||||||
|
},
|
||||||
|
Type: corev1.SecretType(secretType),
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalDynamicSecret.Spec.ManagedSecretReference.CreationPolicy == "Owner" {
|
||||||
|
// Set InfisicalSecret instance as the owner and controller of the managed secret
|
||||||
|
err := ctrl.SetControllerReference(&infisicalDynamicSecret, newKubeSecretInstance, r.Scheme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.Client.Create(ctx, newKubeSecretInstance)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret. [type: %s]", secretType))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) {
|
||||||
|
authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){
|
||||||
|
util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth,
|
||||||
|
util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth,
|
||||||
|
util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth,
|
||||||
|
util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth,
|
||||||
|
util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth,
|
||||||
|
util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth,
|
||||||
|
}
|
||||||
|
|
||||||
|
for authStrategy, authHandler := range authStrategies {
|
||||||
|
authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{
|
||||||
|
Secret: infisicalSecret,
|
||||||
|
Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET,
|
||||||
|
}, infisicalClient)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return authDetails, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, util.ErrAuthNotApplicable) {
|
||||||
|
return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret) (caCertificate string, err error) {
|
||||||
|
|
||||||
|
caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||||
|
Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace,
|
||||||
|
Name: infisicalSecret.Spec.TLS.CaRef.SecretName,
|
||||||
|
})
|
||||||
|
|
||||||
|
if k8Errors.IsNotFound(err) {
|
||||||
|
return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey])
|
||||||
|
|
||||||
|
return caCertificateFromSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) util.ResourceVariables {
|
||||||
|
|
||||||
|
var resourceVariables util.ResourceVariables
|
||||||
|
|
||||||
|
if _, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]; !ok {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{
|
||||||
|
SiteUrl: api.API_HOST_URL,
|
||||||
|
CaCertificate: api.API_CA_CERTIFICATE,
|
||||||
|
UserAgent: api.USER_AGENT_NAME,
|
||||||
|
})
|
||||||
|
|
||||||
|
infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = util.ResourceVariables{
|
||||||
|
InfisicalClient: client,
|
||||||
|
CancelCtx: cancel,
|
||||||
|
AuthDetails: util.AuthenticationDetails{},
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||||
|
|
||||||
|
} else {
|
||||||
|
resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceVariables
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) CreateDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||||
|
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
request := infisicalSdk.CreateDynamicSecretLeaseOptions{
|
||||||
|
DynamicSecretName: infisicalDynamicSecret.Spec.DynamicSecret.SecretName,
|
||||||
|
ProjectSlug: project.Slug,
|
||||||
|
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||||
|
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||||
|
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
leaseData, dynamicSecret, lease, err := infisicalClient.DynamicSecrets().Leases().Create(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create lease [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newLeaseStatus := &v1alpha1.InfisicalDynamicSecretLease{
|
||||||
|
ID: lease.Id,
|
||||||
|
ExpiresAt: metav1.NewTime(lease.ExpireAt),
|
||||||
|
CreationTimestamp: metav1.NewTime(time.Now()),
|
||||||
|
Version: int64(lease.Version),
|
||||||
|
}
|
||||||
|
|
||||||
|
infisicalDynamicSecret.Status.DynamicSecretID = dynamicSecret.Id
|
||||||
|
infisicalDynamicSecret.Status.MaxTTL = dynamicSecret.MaxTTL
|
||||||
|
infisicalDynamicSecret.Status.Lease = newLeaseStatus
|
||||||
|
|
||||||
|
// write the leaseData to the destination secret
|
||||||
|
destinationData := map[string]string{}
|
||||||
|
|
||||||
|
for key, value := range leaseData {
|
||||||
|
if strValue, ok := value.(string); ok {
|
||||||
|
destinationData[key] = strValue
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("unable to convert value to string for key %s", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.StringData = destinationData
|
||||||
|
destination.Annotations[constants.SECRET_VERSION_ANNOTATION] = fmt.Sprintf("%s-%d", lease.Id, lease.Version)
|
||||||
|
|
||||||
|
if err := r.Client.Update(ctx, destination); err != nil {
|
||||||
|
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||||
|
return fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(fmt.Sprintf("New lease successfully created [leaseId=%s]", lease.Id))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) RenewDynamicSecretLease(ctx context.Context, logger logr.Logger, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, destination *corev1.Secret) error {
|
||||||
|
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
request := infisicalSdk.RenewDynamicSecretLeaseOptions{
|
||||||
|
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||||
|
ProjectSlug: project.Slug,
|
||||||
|
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||||
|
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalDynamicSecret.Spec.LeaseTTL != "" {
|
||||||
|
request.TTL = infisicalDynamicSecret.Spec.LeaseTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
lease, err := infisicalClient.DynamicSecrets().Leases().RenewById(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
if strings.Contains(err.Error(), "TTL cannot be larger than max ttl") || // Case 1: TTL is larger than the max TTL
|
||||||
|
strings.Contains(err.Error(), "Dynamic secret lease with ID") { // Case 2: The lease has already expired and has been deleted
|
||||||
|
return constants.ErrInvalidLease
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unable to renew lease [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
infisicalDynamicSecret.Status.Lease.ExpiresAt = metav1.NewTime(lease.ExpireAt)
|
||||||
|
|
||||||
|
// update the infisicalDynamicSecret status
|
||||||
|
if err := r.Client.Status().Update(ctx, infisicalDynamicSecret); err != nil {
|
||||||
|
return fmt.Errorf("unable to update InfisicalDynamicSecret status [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(fmt.Sprintf("Lease successfully renewed [leaseId=%s]", lease.Id))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) updateResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariables util.ResourceVariables) {
|
||||||
|
infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = resourceVariables
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) error {
|
||||||
|
if infisicalDynamicSecret.Spec.LeaseRevocationPolicy != string(constants.DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceVariables := r.getResourceVariables(infisicalDynamicSecret)
|
||||||
|
infisicalClient := resourceVariables.InfisicalClient
|
||||||
|
|
||||||
|
logger.Info("Authenticating for lease revocation")
|
||||||
|
authDetails, err := r.handleAuthentication(ctx, infisicalDynamicSecret, infisicalClient)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.updateResourceVariables(infisicalDynamicSecret, util.ResourceVariables{
|
||||||
|
InfisicalClient: infisicalClient,
|
||||||
|
CancelCtx: resourceVariables.CancelCtx,
|
||||||
|
AuthDetails: authDetails,
|
||||||
|
})
|
||||||
|
|
||||||
|
if infisicalDynamicSecret.Status.Lease == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
project, err := util.GetProjectByID(infisicalClient.Auth().GetAccessToken(), infisicalDynamicSecret.Spec.DynamicSecret.ProjectID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
infisicalClient.DynamicSecrets().Leases().DeleteById(infisicalSdk.DeleteDynamicSecretLeaseOptions{
|
||||||
|
LeaseId: infisicalDynamicSecret.Status.Lease.ID,
|
||||||
|
ProjectSlug: project.Slug,
|
||||||
|
SecretPath: infisicalDynamicSecret.Spec.DynamicSecret.SecretPath,
|
||||||
|
EnvironmentSlug: infisicalDynamicSecret.Spec.DynamicSecret.EnvironmentSlug,
|
||||||
|
})
|
||||||
|
|
||||||
|
// update the destination data to remove the lease data
|
||||||
|
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||||
|
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||||
|
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to fetch destination secret [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.Data = map[string][]byte{}
|
||||||
|
|
||||||
|
if err := r.Client.Update(ctx, destination); err != nil {
|
||||||
|
return fmt.Errorf("unable to update destination secret [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info(fmt.Sprintf("Lease successfully revoked [leaseId=%s]", infisicalDynamicSecret.Status.Lease.ID))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) (time.Duration, error) {
|
||||||
|
|
||||||
|
resourceVariables := r.getResourceVariables(infisicalDynamicSecret)
|
||||||
|
infisicalClient := resourceVariables.InfisicalClient
|
||||||
|
cancelCtx := resourceVariables.CancelCtx
|
||||||
|
authDetails := resourceVariables.AuthDetails
|
||||||
|
|
||||||
|
defaultNextReconcile := 5 * time.Second
|
||||||
|
nextReconcile := defaultNextReconcile
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if authDetails.AuthStrategy == "" {
|
||||||
|
logger.Info("No authentication strategy found. Attempting to authenticate")
|
||||||
|
authDetails, err = r.handleAuthentication(ctx, infisicalDynamicSecret, infisicalClient)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.updateResourceVariables(infisicalDynamicSecret, util.ResourceVariables{
|
||||||
|
InfisicalClient: infisicalClient,
|
||||||
|
CancelCtx: cancelCtx,
|
||||||
|
AuthDetails: authDetails,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||||
|
Name: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretName,
|
||||||
|
Namespace: infisicalDynamicSecret.Spec.ManagedSecretReference.SecretNamespace,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil && !k8Errors.IsNotFound(err) {
|
||||||
|
annotationValue := ""
|
||||||
|
if infisicalDynamicSecret.Status.Lease != nil {
|
||||||
|
annotationValue = fmt.Sprintf("%s-%d", infisicalDynamicSecret.Status.Lease.ID, infisicalDynamicSecret.Status.Lease.Version)
|
||||||
|
}
|
||||||
|
r.createInfisicalManagedKubeSecret(ctx, logger, infisicalDynamicSecret, annotationValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if k8Errors.IsNotFound(err) {
|
||||||
|
return nextReconcile, fmt.Errorf("destination secret not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextReconcile, fmt.Errorf("unable to fetch destination secret")
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalDynamicSecret.Status.Lease == nil {
|
||||||
|
r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||||
|
} else {
|
||||||
|
now := time.Now()
|
||||||
|
leaseExpiresAt := infisicalDynamicSecret.Status.Lease.ExpiresAt.Time
|
||||||
|
|
||||||
|
// Calculate from creation to expiration
|
||||||
|
originalLeaseDuration := leaseExpiresAt.Sub(infisicalDynamicSecret.Status.Lease.CreationTimestamp.Time)
|
||||||
|
|
||||||
|
// 30% of the original duration (if the TTL has 30% or less of its time left, renew)
|
||||||
|
renewalThreshold := originalLeaseDuration * 30 / 100
|
||||||
|
timeUntilExpiration := time.Until(leaseExpiresAt)
|
||||||
|
|
||||||
|
nextReconcile = timeUntilExpiration / 2
|
||||||
|
|
||||||
|
// Max TTL
|
||||||
|
if infisicalDynamicSecret.Status.MaxTTL != "" {
|
||||||
|
maxTTLDuration, err := util.ConvertIntervalToDuration(infisicalDynamicSecret.Status.MaxTTL)
|
||||||
|
if err != nil {
|
||||||
|
return defaultNextReconcile, fmt.Errorf("unable to parse MaxTTL duration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate when this dynamic secret will hit its max TTL
|
||||||
|
maxTTLExpirationTime := infisicalDynamicSecret.Status.Lease.CreationTimestamp.Add(maxTTLDuration)
|
||||||
|
|
||||||
|
// Calculate remaining time until max TTL
|
||||||
|
timeUntilMaxTTL := maxTTLExpirationTime.Sub(now)
|
||||||
|
maxTTLThreshold := maxTTLDuration * 40 / 100
|
||||||
|
|
||||||
|
// If we have less than 40% of max TTL remaining or have exceeded it, create new lease
|
||||||
|
if timeUntilMaxTTL <= maxTTLThreshold || now.After(maxTTLExpirationTime) {
|
||||||
|
logger.Info(fmt.Sprintf("Approaching or exceeded max TTL [timeUntilMaxTTL=%v] [maxTTLThreshold=%v], creating new lease...",
|
||||||
|
timeUntilMaxTTL,
|
||||||
|
maxTTLThreshold))
|
||||||
|
|
||||||
|
err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||||
|
return defaultNextReconcile, err // Short requeue after creation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail-safe: If the lease has expired we create a new dynamic secret directly.
|
||||||
|
if now.After(leaseExpiresAt) {
|
||||||
|
logger.Info("Lease has expired, creating new lease...")
|
||||||
|
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||||
|
return defaultNextReconcile, err // Short requeue after creation
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeUntilExpiration < renewalThreshold {
|
||||||
|
logger.Info(fmt.Sprintf("Lease renewal needed [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||||
|
infisicalDynamicSecret.Status.Lease.ID,
|
||||||
|
timeUntilExpiration,
|
||||||
|
renewalThreshold))
|
||||||
|
|
||||||
|
err = r.RenewDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||||
|
|
||||||
|
if err == constants.ErrInvalidLease {
|
||||||
|
logger.Info("Failed to renew expired lease, creating new lease...")
|
||||||
|
err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, &infisicalDynamicSecret, destination)
|
||||||
|
}
|
||||||
|
return defaultNextReconcile, err // Short requeue after renewal/creation
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.Info(fmt.Sprintf("Lease renewal not needed yet [leaseId=%s] [timeUntilExpiration=%v] [threshold=%v]",
|
||||||
|
infisicalDynamicSecret.Status.Lease.ID,
|
||||||
|
timeUntilExpiration,
|
||||||
|
renewalThreshold))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small buffer (20% of the calculated time) to ensure we don't cut it too close
|
||||||
|
nextReconcile = nextReconcile * 8 / 10
|
||||||
|
|
||||||
|
// Minimum and maximum bounds for the reconcile interval (5 min max, 5 min minimum)
|
||||||
|
nextReconcile = max(5*time.Second, min(nextReconcile, 5*time.Minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextReconcile, nil
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ import (
|
|||||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
||||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerutil"
|
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
)
|
)
|
||||||
@@ -105,7 +105,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
|
|||||||
|
|
||||||
if infisicalPushSecretCRD.Spec.ResyncInterval != "" {
|
if infisicalPushSecretCRD.Spec.ResyncInterval != "" {
|
||||||
|
|
||||||
duration, err := util.ConvertResyncIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
|
duration, err := util.ConvertIntervalToDuration(infisicalPushSecretCRD.Spec.ResyncInterval)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, fmt.Sprintf("unable to convert resync interval to duration. Will requeue after [requeueTime=%v]", requeueTime))
|
logger.Error(err, fmt.Sprintf("unable to convert resync interval to duration. Will requeue after [requeueTime=%v]", requeueTime))
|
||||||
@@ -141,7 +141,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
|
|||||||
if infisicalPushSecretCRD.Spec.HostAPI == "" {
|
if infisicalPushSecretCRD.Spec.HostAPI == "" {
|
||||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||||
} else {
|
} else {
|
||||||
api.API_HOST_URL = infisicalPushSecretCRD.Spec.HostAPI
|
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalPushSecretCRD.Spec.HostAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
if infisicalPushSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
if infisicalPushSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerutil"
|
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/util"
|
"github.com/Infisical/infisical/k8-operator/packages/util"
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
)
|
)
|
||||||
@@ -110,7 +110,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
if infisicalSecretCRD.Spec.HostAPI == "" {
|
if infisicalSecretCRD.Spec.HostAPI == "" {
|
||||||
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
api.API_HOST_URL = infisicalConfig["hostAPI"]
|
||||||
} else {
|
} else {
|
||||||
api.API_HOST_URL = infisicalSecretCRD.Spec.HostAPI
|
api.API_HOST_URL = util.AppendAPIEndpoint(infisicalSecretCRD.Spec.HostAPI)
|
||||||
}
|
}
|
||||||
|
|
||||||
if infisicalSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
if infisicalSecretCRD.Spec.TLS.CaRef.SecretName != "" {
|
||||||
@@ -138,7 +138,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
numDeployments, err := r.ReconcileDeploymentsWithManagedSecrets(ctx, logger, infisicalSecretCRD)
|
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalSecretCRD.Spec.ManagedSecretReference)
|
||||||
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@ module github.com/Infisical/infisical/k8-operator
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/infisical/go-sdk v0.4.1
|
github.com/infisical/go-sdk v0.4.4
|
||||||
github.com/onsi/ginkgo/v2 v2.6.0
|
github.com/onsi/ginkgo/v2 v2.6.0
|
||||||
github.com/onsi/gomega v1.24.1
|
github.com/onsi/gomega v1.24.1
|
||||||
k8s.io/apimachinery v0.26.1
|
k8s.io/apimachinery v0.26.1
|
||||||
@@ -54,7 +54,7 @@ require (
|
|||||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
github.com/go-logr/logr v1.4.2
|
||||||
github.com/go-logr/zapr v1.2.3 // indirect
|
github.com/go-logr/zapr v1.2.3 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||||
|
|||||||
@@ -219,6 +219,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
|||||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
github.com/infisical/go-sdk v0.4.1 h1:ZeLyc2+2TeIaw9odjxR3ipQqYzVSMOnd8/RaqyUNvBg=
|
github.com/infisical/go-sdk v0.4.1 h1:ZeLyc2+2TeIaw9odjxR3ipQqYzVSMOnd8/RaqyUNvBg=
|
||||||
github.com/infisical/go-sdk v0.4.1/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
github.com/infisical/go-sdk v0.4.1/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||||
|
github.com/infisical/go-sdk v0.4.3 h1:O5ZJ2eCBAZDE9PIAfBPq9Utb2CgQKrhmj9R0oFTRu4U=
|
||||||
|
github.com/infisical/go-sdk v0.4.3/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||||
|
github.com/infisical/go-sdk v0.4.4 h1:Z4CBzxfhiY6ikjRimOEeyEEnb3QT/BKw3OzNFH7Pe+U=
|
||||||
|
github.com/infisical/go-sdk v0.4.4/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
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 controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object
|
||||||
|
type InfisicalDynamicSecretReconciler struct {
|
||||||
|
client.Client
|
||||||
|
Scheme *runtime.Scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch
|
||||||
|
// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update
|
||||||
|
|
||||||
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
|
// move the current state of the cluster closer to the desired state.
|
||||||
|
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||||
|
// the InfisicalDynamicSecret object against the actual cluster state, and then
|
||||||
|
// perform operations to make the cluster state reflect the state specified by
|
||||||
|
// the user.
|
||||||
|
//
|
||||||
|
// For more details, check Reconcile and its Result here:
|
||||||
|
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
|
_ = log.FromContext(ctx)
|
||||||
|
|
||||||
|
// TODO(user): your logic here
|
||||||
|
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupWithManager sets up the controller with the Manager.
|
||||||
|
func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||||
|
return ctrl.NewControllerManagedBy(mgr).
|
||||||
|
For(&secretsv1alpha1.InfisicalDynamicSecret{}).
|
||||||
|
Named("infisicaldynamicsecret").
|
||||||
|
Complete(r)
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||||
|
|
||||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
|
infisicalDynamicSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicaldynamicsecret"
|
||||||
infisicalPushSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalpushsecret"
|
infisicalPushSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalpushsecret"
|
||||||
infisicalSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalsecret"
|
infisicalSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalsecret"
|
||||||
//+kubebuilder:scaffold:imports
|
//+kubebuilder:scaffold:imports
|
||||||
@@ -92,6 +93,15 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = (&infisicalDynamicSecretController.InfisicalDynamicSecretReconciler{
|
||||||
|
Client: mgr.GetClient(),
|
||||||
|
Scheme: mgr.GetScheme(),
|
||||||
|
BaseLogger: ctrl.Log,
|
||||||
|
}).SetupWithManager(mgr); err != nil {
|
||||||
|
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
//+kubebuilder:scaffold:builder
|
//+kubebuilder:scaffold:builder
|
||||||
|
|
||||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||||
|
|||||||
@@ -125,3 +125,24 @@ func CallGetServiceAccountKeysV2(httpClient *resty.Client, request GetServiceAcc
|
|||||||
|
|
||||||
return serviceAccountKeysResponse, nil
|
return serviceAccountKeysResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CallGetProjectByID(httpClient *resty.Client, request GetProjectByIDRequest) (GetProjectByIDResponse, error) {
|
||||||
|
|
||||||
|
var projectResponse GetProjectByIDResponse
|
||||||
|
|
||||||
|
response, err := httpClient.
|
||||||
|
R().SetResult(&projectResponse).
|
||||||
|
SetHeader("User-Agent", USER_AGENT_NAME).
|
||||||
|
Get(fmt.Sprintf("%s/v1/workspace/%s", API_HOST_URL, request.ProjectID))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.IsError() {
|
||||||
|
return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectResponse, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/model"
|
||||||
|
)
|
||||||
|
|
||||||
type GetEncryptedWorkspaceKeyRequest struct {
|
type GetEncryptedWorkspaceKeyRequest struct {
|
||||||
WorkspaceId string `json:"workspaceId"`
|
WorkspaceId string `json:"workspaceId"`
|
||||||
@@ -194,3 +198,11 @@ type ServiceAccountKey struct {
|
|||||||
type GetServiceAccountKeysResponse struct {
|
type GetServiceAccountKeysResponse struct {
|
||||||
ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"`
|
ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetProjectByIDRequest struct {
|
||||||
|
ProjectID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetProjectByIDResponse struct {
|
||||||
|
Project model.Project `json:"workspace"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey"
|
const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey"
|
||||||
const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey"
|
const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey"
|
||||||
const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey"
|
const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey"
|
||||||
@@ -14,6 +16,7 @@ const OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE = "infisical-operator-system"
|
|||||||
const INFISICAL_DOMAIN = "https://app.infisical.com/api"
|
const INFISICAL_DOMAIN = "https://app.infisical.com/api"
|
||||||
|
|
||||||
const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer"
|
const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer"
|
||||||
|
const INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME = "dynamicsecret.secrets.infisical.com/finalizer"
|
||||||
|
|
||||||
type PushSecretReplacePolicy string
|
type PushSecretReplacePolicy string
|
||||||
type PushSecretDeletionPolicy string
|
type PushSecretDeletionPolicy string
|
||||||
@@ -22,3 +25,11 @@ const (
|
|||||||
PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace"
|
PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace"
|
||||||
PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete"
|
PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DynamicSecretLeaseRevocationPolicy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED DynamicSecretLeaseRevocationPolicy = "Revoke"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInvalidLease = errors.New("invalid dynamic secret lease")
|
||||||
|
|||||||
+50
-14
@@ -1,4 +1,4 @@
|
|||||||
package controllers
|
package controllerhelpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,27 +10,30 @@ import (
|
|||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
controllerClient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
|
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
|
||||||
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
|
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret) (int, error) {
|
func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) {
|
||||||
listOfDeployments := &v1.DeploymentList{}
|
listOfDeployments := &v1.DeploymentList{}
|
||||||
err := r.Client.List(ctx, listOfDeployments, &client.ListOptions{Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace})
|
|
||||||
|
err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", infisicalSecret.Spec.ManagedSecretReference.SecretNamespace, err)
|
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
managedKubeSecretNameAndNamespace := types.NamespacedName{
|
managedKubeSecretNameAndNamespace := types.NamespacedName{
|
||||||
Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
|
Namespace: managedSecret.SecretNamespace,
|
||||||
Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
|
Name: managedSecret.SecretName,
|
||||||
}
|
}
|
||||||
|
|
||||||
managedKubeSecret := &corev1.Secret{}
|
managedKubeSecret := &corev1.Secret{}
|
||||||
err = r.Client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
|
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
|
||||||
}
|
}
|
||||||
@@ -39,12 +42,12 @@ func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx c
|
|||||||
// Iterate over the deployments and check if they use the managed secret
|
// Iterate over the deployments and check if they use the managed secret
|
||||||
for _, deployment := range listOfDeployments.Items {
|
for _, deployment := range listOfDeployments.Items {
|
||||||
deployment := deployment
|
deployment := deployment
|
||||||
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && r.IsDeploymentUsingManagedSecret(deployment, infisicalSecret) {
|
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDeploymentUsingManagedSecret(deployment, managedSecret) {
|
||||||
// Start a goroutine to reconcile the deployment
|
// Start a goroutine to reconcile the deployment
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(d v1.Deployment, s corev1.Secret) {
|
go func(deployment v1.Deployment, managedSecret corev1.Secret) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := r.ReconcileDeployment(ctx, logger, d, s); err != nil {
|
if err := ReconcileDeployment(ctx, client, logger, deployment, managedSecret); err != nil {
|
||||||
logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
||||||
}
|
}
|
||||||
}(deployment, *managedKubeSecret)
|
}(deployment, *managedKubeSecret)
|
||||||
@@ -57,8 +60,8 @@ func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the deployment uses managed secrets
|
// Check if the deployment uses managed secrets
|
||||||
func (r *InfisicalSecretReconciler) IsDeploymentUsingManagedSecret(deployment v1.Deployment, infisicalSecret v1alpha1.InfisicalSecret) bool {
|
func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||||
managedSecretName := infisicalSecret.Spec.ManagedSecretReference.SecretName
|
managedSecretName := managedSecret.SecretName
|
||||||
for _, container := range deployment.Spec.Template.Spec.Containers {
|
for _, container := range deployment.Spec.Template.Spec.Containers {
|
||||||
for _, envFrom := range container.EnvFrom {
|
for _, envFrom := range container.EnvFrom {
|
||||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
||||||
@@ -82,7 +85,7 @@ func (r *InfisicalSecretReconciler) IsDeploymentUsingManagedSecret(deployment v1
|
|||||||
|
|
||||||
// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions.
|
// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions.
|
||||||
// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment.
|
// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment.
|
||||||
func (r *InfisicalSecretReconciler) ReconcileDeployment(ctx context.Context, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
func ReconcileDeployment(ctx context.Context, client controllerClient.Client, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
||||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
||||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
||||||
|
|
||||||
@@ -101,8 +104,41 @@ func (r *InfisicalSecretReconciler) ReconcileDeployment(ctx context.Context, log
|
|||||||
deployment.Annotations[annotationKey] = annotationValue
|
deployment.Annotations[annotationKey] = annotationValue
|
||||||
deployment.Spec.Template.Annotations[annotationKey] = annotationValue
|
deployment.Spec.Template.Annotations[annotationKey] = annotationValue
|
||||||
|
|
||||||
if err := r.Client.Update(ctx, &deployment); err != nil {
|
if err := client.Update(ctx, &deployment); err != nil {
|
||||||
return fmt.Errorf("failed to update deployment annotation: %v", err)
|
return fmt.Errorf("failed to update deployment annotation: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
|
||||||
|
// default key values
|
||||||
|
defaultConfigMapData := make(map[string]string)
|
||||||
|
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
|
||||||
|
|
||||||
|
kubeConfigMap := &corev1.ConfigMap{}
|
||||||
|
err := client.Get(ctx, types.NamespacedName{
|
||||||
|
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
|
||||||
|
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
|
||||||
|
}, kubeConfigMap)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if k8Errors.IsNotFound(err) {
|
||||||
|
kubeConfigMap = nil
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kubeConfigMap == nil {
|
||||||
|
return defaultConfigMapData, nil
|
||||||
|
} else {
|
||||||
|
for key, value := range defaultConfigMapData {
|
||||||
|
_, exists := kubeConfigMap.Data[key]
|
||||||
|
if !exists {
|
||||||
|
kubeConfigMap.Data[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return kubeConfigMap.Data, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package controllerhelpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
k8Errors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) {
|
|
||||||
// default key values
|
|
||||||
defaultConfigMapData := make(map[string]string)
|
|
||||||
defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN
|
|
||||||
|
|
||||||
kubeConfigMap := &corev1.ConfigMap{}
|
|
||||||
err := client.Get(ctx, types.NamespacedName{
|
|
||||||
Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE,
|
|
||||||
Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME,
|
|
||||||
}, kubeConfigMap)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if k8Errors.IsNotFound(err) {
|
|
||||||
kubeConfigMap = nil
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if kubeConfigMap == nil {
|
|
||||||
return defaultConfigMapData, nil
|
|
||||||
} else {
|
|
||||||
for key, value := range defaultConfigMapData {
|
|
||||||
_, exists := kubeConfigMap.Data[key]
|
|
||||||
if !exists {
|
|
||||||
kubeConfigMap.Data[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return kubeConfigMap.Data, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,3 +28,15 @@ type SecretTemplateOptions struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
SecretPath string `json:"secretPath"`
|
SecretPath string `json:"secretPath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Project struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
Environments []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,9 +65,11 @@ type SecretCrdType string
|
|||||||
var SecretCrd = struct {
|
var SecretCrd = struct {
|
||||||
INFISICAL_SECRET SecretCrdType
|
INFISICAL_SECRET SecretCrdType
|
||||||
INFISICAL_PUSH_SECRET SecretCrdType
|
INFISICAL_PUSH_SECRET SecretCrdType
|
||||||
|
INFISICAL_DYNAMIC_SECRET SecretCrdType
|
||||||
}{
|
}{
|
||||||
INFISICAL_SECRET: "INFISICAL_SECRET",
|
INFISICAL_SECRET: "INFISICAL_SECRET",
|
||||||
INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET",
|
INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET",
|
||||||
|
INFISICAL_DYNAMIC_SECRET: "INFISICAL_DYNAMIC_SECRET",
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecretAuthInput struct {
|
type SecretAuthInput struct {
|
||||||
@@ -107,6 +109,18 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se
|
|||||||
CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
universalAuthSpec = v1alpha1.UniversalAuthDetails{
|
||||||
|
CredentialsRef: infisicalDynamicSecret.Spec.Authentication.UniversalAuth.CredentialsRef,
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
|
universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{
|
||||||
@@ -160,6 +174,22 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s
|
|||||||
},
|
},
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{
|
||||||
|
IdentityID: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.IdentityID,
|
||||||
|
ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{
|
||||||
|
Namespace: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace,
|
||||||
|
Name: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name,
|
||||||
|
},
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if kubernetesAuthSpec.IdentityID == "" {
|
if kubernetesAuthSpec.IdentityID == "" {
|
||||||
@@ -208,6 +238,18 @@ func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secre
|
|||||||
IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{
|
||||||
|
IdentityID: infisicalDynamicSecret.Spec.Authentication.AwsIamAuth.IdentityID,
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if awsIamAuthSpec.IdentityID == "" {
|
if awsIamAuthSpec.IdentityID == "" {
|
||||||
@@ -253,6 +295,19 @@ func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secret
|
|||||||
Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource,
|
Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource,
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
azureAuthSpec = v1alpha1.AzureAuthDetails{
|
||||||
|
IdentityID: infisicalDynamicSecret.Spec.Authentication.AzureAuth.IdentityID,
|
||||||
|
Resource: infisicalDynamicSecret.Spec.Authentication.AzureAuth.Resource,
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if azureAuthSpec.IdentityID == "" {
|
if azureAuthSpec.IdentityID == "" {
|
||||||
@@ -296,6 +351,18 @@ func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, s
|
|||||||
IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{
|
||||||
|
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID,
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if gcpIdTokenSpec.IdentityID == "" {
|
if gcpIdTokenSpec.IdentityID == "" {
|
||||||
@@ -340,6 +407,19 @@ func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secre
|
|||||||
ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||||
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SecretCrd.INFISICAL_DYNAMIC_SECRET:
|
||||||
|
infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
gcpIamSpec = v1alpha1.GcpIamAuthDetails{
|
||||||
|
IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.IdentityID,
|
||||||
|
ServiceAccountKeyFilePath: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath,
|
||||||
|
SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" {
|
if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" {
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package util
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, error) {
|
func ConvertIntervalToDuration(resyncInterval string) (time.Duration, error) {
|
||||||
length := len(resyncInterval)
|
length := len(resyncInterval)
|
||||||
if length < 2 {
|
if length < 2 {
|
||||||
return 0, fmt.Errorf("invalid format")
|
return 0, fmt.Errorf("invalid format")
|
||||||
@@ -38,3 +39,23 @@ func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, erro
|
|||||||
return 0, fmt.Errorf("invalid time unit")
|
return 0, fmt.Errorf("invalid time unit")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
if address[len(address)-1] == '/' {
|
||||||
|
return address + "api"
|
||||||
|
}
|
||||||
|
return address + "/api"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/model"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetProjectByID(accessToken string, projectId string) (model.Project, error) {
|
||||||
|
|
||||||
|
httpClient := resty.New()
|
||||||
|
httpClient.
|
||||||
|
SetAuthScheme("Bearer").
|
||||||
|
SetAuthToken(accessToken).
|
||||||
|
SetHeader("Accept", "application/json")
|
||||||
|
|
||||||
|
projectDetails, err := api.CallGetProjectByID(httpClient, api.GetProjectByIDRequest{
|
||||||
|
ProjectID: projectId,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return model.Project{}, fmt.Errorf("unable to get project by slug. [err=%v]", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectDetails.Project, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user