mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(k8s): pushsecret go templating
This commit is contained in:
@@ -401,6 +401,82 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
|
|
||||||
|
## Using templating to push secrets
|
||||||
|
|
||||||
|
Pushing secrets to Infisical from the operator may not always be enough.
|
||||||
|
Templating is a useful utility of the Infisical secrets operator that allows you to use Go Templating to template the secrets you want to push to Infisical.
|
||||||
|
Using Go templates, you can format, combine, and create new key-value pairs of secrets that you want to push to Infisical.
|
||||||
|
|
||||||
|
<Accordion title="push.secret.template"/>
|
||||||
|
<Accordion title="push.secret.template.includeAllSecrets">
|
||||||
|
This property controls what secrets are included in your push to Infisica.
|
||||||
|
When set to `true`, all secrets included in the `push.secret.secretName` Kubernetes secret will be pushed to Infisical.
|
||||||
|
**Use this option when you would like to push all secrets to Infisical from the secrets operator, but want to template a subset of them.**
|
||||||
|
|
||||||
|
When set to `false`, only secrets defined in the `push.secret.template.data` field of the template will be pushed to Infisical.
|
||||||
|
Use this option when you would like to push **only** a subset of secrets from the Kubernetes secret to Infisical.
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="push.secret.template.data">
|
||||||
|
Define secret keys and their corresponding templates.
|
||||||
|
Each data value uses a Golang template with access to all secrets defined in the `push.secret.secretName` Kubernetes secret.
|
||||||
|
|
||||||
|
Secrets are structured as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type TemplateSecret struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
SecretPath string `json:"secretPath"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example template configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# This example assumes that the `push-secret-demo` Kubernetes secret contains the following secrets:
|
||||||
|
# SITE_URL = "https://example.com"
|
||||||
|
# REGION = "us-east-1"
|
||||||
|
# OTHER_SECRET = "other-secret"
|
||||||
|
|
||||||
|
push:
|
||||||
|
secret:
|
||||||
|
secretName: push-secret-demo
|
||||||
|
secretNamespace: default
|
||||||
|
template:
|
||||||
|
includeAllSecrets: true # Includes all secrets from the `push-secret-demo` Kubernetes secret
|
||||||
|
data:
|
||||||
|
SITE_URL: "{{ .SITE_URL.Value }}"
|
||||||
|
API_URL: "https://api.{{.SITE_URL.Value}}.{{.REGION.Value}}.com" # Will create a new secret in Infisical with the key `API_URL` with the value of the `SITE_URL` and `REGION` secrets
|
||||||
|
```
|
||||||
|
|
||||||
|
To help transform your config map data further, the operator provides a set of built-in functions that you can use in your templates.
|
||||||
|
|
||||||
|
### Available templating functions
|
||||||
|
|
||||||
|
<Accordion title="encodeBase64">
|
||||||
|
**Function name**: encodeBase64
|
||||||
|
|
||||||
|
**Description**:
|
||||||
|
Given a string, this function will encode the string as a base64 encoded string.
|
||||||
|
This function is useful when you want to store a string as a base64 encoded value in Infisical.
|
||||||
|
|
||||||
|
**Returns**: The base64 encoded string.
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
The example below assumes that the `PLAIN_KEY` secret is stored in your source secret as a plaintext string.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
push:
|
||||||
|
secret:
|
||||||
|
secretName: push-secret-demo
|
||||||
|
secretNamespace: default
|
||||||
|
template:
|
||||||
|
includeAllSecrets: true
|
||||||
|
data:
|
||||||
|
PLAIN_KEY: "{{ encodeBase64 .PLAIN_KEY.Value }}" # Will be stored in Infisical as a base64 encoded string
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
## Applying the InfisicalPushSecret CRD to your cluster
|
## Applying the InfisicalPushSecret CRD to your cluster
|
||||||
|
|
||||||
Once you have configured the `InfisicalPushSecret` CRD with the required fields, you can apply it to your cluster.
|
Once you have configured the `InfisicalPushSecret` CRD with the required fields, you can apply it to your cluster.
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ type ManagedKubeSecretConfig struct {
|
|||||||
|
|
||||||
// The template to transform the secret data
|
// The template to transform the secret data
|
||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
Template *InfisicalSecretTemplate `json:"template,omitempty"`
|
Template *SecretTemplate `json:"template,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManagedKubeConfigMapConfig struct {
|
type ManagedKubeConfigMapConfig struct {
|
||||||
@@ -127,5 +127,15 @@ type ManagedKubeConfigMapConfig struct {
|
|||||||
|
|
||||||
// The template to transform the secret data
|
// The template to transform the secret data
|
||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
Template *InfisicalSecretTemplate `json:"template,omitempty"`
|
Template *SecretTemplate `json:"template,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SecretTemplate struct {
|
||||||
|
// This injects all retrieved secrets into the top level of your template.
|
||||||
|
// Secrets defined in the template will take precedence over the injected ones.
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
IncludeAllSecrets bool `json:"includeAllSecrets"`
|
||||||
|
// The template key values
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
Data map[string]string `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,22 @@ type InfisicalPushSecretDestination struct {
|
|||||||
ProjectID string `json:"projectId"`
|
ProjectID string `json:"projectId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InfisicalPushSecretSecretSource 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"`
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
Template *SecretTemplate `json:"template,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type SecretPush struct {
|
type SecretPush struct {
|
||||||
// +kubebuilder:validation:Required
|
// +kubebuilder:validation:Required
|
||||||
Secret KubeSecretReference `json:"secret"`
|
Secret InfisicalPushSecretSecretSource `json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
|
// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
|
||||||
|
|||||||
@@ -116,16 +116,6 @@ type MachineIdentityScopeInWorkspace struct {
|
|||||||
Recursive bool `json:"recursive"`
|
Recursive bool `json:"recursive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InfisicalSecretTemplate struct {
|
|
||||||
// This injects all retrieved secrets into the top level of your template.
|
|
||||||
// Secrets defined in the template will take precedence over the injected ones.
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
IncludeAllSecrets bool `json:"includeAllSecrets"`
|
|
||||||
// The template key values
|
|
||||||
// +kubebuilder:validation:Optional
|
|
||||||
Data map[string]string `json:"data,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
|
||||||
|
|||||||
@@ -383,7 +383,7 @@ func (in *InfisicalPushSecret) DeepCopyInto(out *InfisicalPushSecret) {
|
|||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
out.Spec = in.Spec
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
in.Status.DeepCopyInto(&out.Status)
|
in.Status.DeepCopyInto(&out.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,12 +452,32 @@ func (in *InfisicalPushSecretList) DeepCopyObject() runtime.Object {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InfisicalPushSecretSecretSource) DeepCopyInto(out *InfisicalPushSecretSecretSource) {
|
||||||
|
*out = *in
|
||||||
|
if in.Template != nil {
|
||||||
|
in, out := &in.Template, &out.Template
|
||||||
|
*out = new(SecretTemplate)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretSource.
|
||||||
|
func (in *InfisicalPushSecretSecretSource) DeepCopy() *InfisicalPushSecretSecretSource {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InfisicalPushSecretSecretSource)
|
||||||
|
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 *InfisicalPushSecretSpec) DeepCopyInto(out *InfisicalPushSecretSpec) {
|
func (in *InfisicalPushSecretSpec) DeepCopyInto(out *InfisicalPushSecretSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.Destination = in.Destination
|
out.Destination = in.Destination
|
||||||
out.Authentication = in.Authentication
|
out.Authentication = in.Authentication
|
||||||
out.Push = in.Push
|
in.Push.DeepCopyInto(&out.Push)
|
||||||
out.TLS = in.TLS
|
out.TLS = in.TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,28 +634,6 @@ func (in *InfisicalSecretStatus) DeepCopy() *InfisicalSecretStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *InfisicalSecretTemplate) DeepCopyInto(out *InfisicalSecretTemplate) {
|
|
||||||
*out = *in
|
|
||||||
if in.Data != nil {
|
|
||||||
in, out := &in.Data, &out.Data
|
|
||||||
*out = make(map[string]string, len(*in))
|
|
||||||
for key, val := range *in {
|
|
||||||
(*out)[key] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretTemplate.
|
|
||||||
func (in *InfisicalSecretTemplate) DeepCopy() *InfisicalSecretTemplate {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(InfisicalSecretTemplate)
|
|
||||||
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 *KubeSecretReference) DeepCopyInto(out *KubeSecretReference) {
|
func (in *KubeSecretReference) DeepCopyInto(out *KubeSecretReference) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -703,7 +701,7 @@ func (in *ManagedKubeConfigMapConfig) DeepCopyInto(out *ManagedKubeConfigMapConf
|
|||||||
*out = *in
|
*out = *in
|
||||||
if in.Template != nil {
|
if in.Template != nil {
|
||||||
in, out := &in.Template, &out.Template
|
in, out := &in.Template, &out.Template
|
||||||
*out = new(InfisicalSecretTemplate)
|
*out = new(SecretTemplate)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,7 +721,7 @@ 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
|
||||||
*out = new(InfisicalSecretTemplate)
|
*out = new(SecretTemplate)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -741,7 +739,7 @@ func (in *ManagedKubeSecretConfig) DeepCopy() *ManagedKubeSecretConfig {
|
|||||||
// 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 *SecretPush) DeepCopyInto(out *SecretPush) {
|
func (in *SecretPush) DeepCopyInto(out *SecretPush) {
|
||||||
*out = *in
|
*out = *in
|
||||||
out.Secret = in.Secret
|
in.Secret.DeepCopyInto(&out.Secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPush.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPush.
|
||||||
@@ -769,6 +767,28 @@ func (in *SecretScopeInWorkspace) DeepCopy() *SecretScopeInWorkspace {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *SecretTemplate) DeepCopyInto(out *SecretTemplate) {
|
||||||
|
*out = *in
|
||||||
|
if in.Data != nil {
|
||||||
|
in, out := &in.Data, &out.Data
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretTemplate.
|
||||||
|
func (in *SecretTemplate) DeepCopy() *SecretTemplate {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(SecretTemplate)
|
||||||
|
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 *ServiceAccountDetails) DeepCopyInto(out *ServiceAccountDetails) {
|
func (in *ServiceAccountDetails) DeepCopyInto(out *ServiceAccountDetails) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
|||||||
@@ -137,6 +137,19 @@ spec:
|
|||||||
description: The name space where the Kubernetes Secret is
|
description: The name space where the Kubernetes Secret is
|
||||||
located
|
located
|
||||||
type: string
|
type: string
|
||||||
|
template:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: The template key values
|
||||||
|
type: object
|
||||||
|
includeAllSecrets:
|
||||||
|
description: This injects all retrieved secrets into the
|
||||||
|
top level of your template. Secrets defined in the template
|
||||||
|
will take precedence over the injected ones.
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
- secretNamespace
|
- secretNamespace
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
apiVersion: secrets.infisical.com/v1alpha1
|
||||||
|
kind: InfisicalPushSecret
|
||||||
|
metadata:
|
||||||
|
name: infisical-api-secret-sample-push
|
||||||
|
spec:
|
||||||
|
resyncInterval: 1m
|
||||||
|
hostAPI: http://localhost:8080/api
|
||||||
|
|
||||||
|
# Optional, defaults to replacement.
|
||||||
|
updatePolicy: Replace # If set to replace, existing secrets inside Infisical will be replaced by the value of the PushSecret on sync.
|
||||||
|
|
||||||
|
# Optional, defaults to no deletion.
|
||||||
|
deletionPolicy: Delete # If set to delete, the secret(s) inside Infisical managed by the operator, will be deleted if the InfisicalPushSecret CRD is deleted.
|
||||||
|
|
||||||
|
destination:
|
||||||
|
projectId: <project-id>
|
||||||
|
environmentSlug: <env-slug>
|
||||||
|
secretsPath: <secret-path>
|
||||||
|
|
||||||
|
push:
|
||||||
|
secret:
|
||||||
|
secretName: push-secret-demo # Secret CRD
|
||||||
|
secretNamespace: default
|
||||||
|
template:
|
||||||
|
includeAllSecrets: false
|
||||||
|
data:
|
||||||
|
# Encodes the data so it's stored as base64 in Infisical.
|
||||||
|
API_KEY: "{{ .API_KEY.Value }}"
|
||||||
|
DATABASE_URL: "{{ .DATABASE_URL.Value }}"
|
||||||
|
ENCRYPTION_KEY: "{{ .ENCRYPTION_KEY.Value }}"
|
||||||
|
OTHER_VALUE: "{{ encodeBase64 .API_KEY.Value }} {{ encodeBase64 .DATABASE_URL.Value }} {{ encodeBase64 .ENCRYPTION_KEY.Value }}"
|
||||||
|
|
||||||
|
# Only have one authentication method defined or you are likely to run into authentication issues.
|
||||||
|
# Remove all except one authentication method.
|
||||||
|
authentication:
|
||||||
|
universalAuth:
|
||||||
|
credentialsRef:
|
||||||
|
secretName: universal-auth-credentials
|
||||||
|
secretNamespace: default
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
"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"
|
||||||
|
"github.com/Infisical/infisical/k8-operator/packages/model"
|
||||||
"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"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
@@ -101,6 +105,48 @@ func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSec
|
|||||||
infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables
|
infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *InfisicalPushSecretReconciler) processTemplatedSecrets(infisicalPushSecret v1alpha1.InfisicalPushSecret, kubePushSecret *corev1.Secret, destination v1alpha1.InfisicalPushSecretDestination) (map[string]string, error) {
|
||||||
|
|
||||||
|
processedSecrets := make(map[string]string)
|
||||||
|
|
||||||
|
sourceSecrets := make(map[string]model.SecretTemplateOptions)
|
||||||
|
for key, value := range kubePushSecret.Data {
|
||||||
|
|
||||||
|
sourceSecrets[key] = model.SecretTemplateOptions{
|
||||||
|
Value: string(value),
|
||||||
|
SecretPath: destination.SecretsPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalPushSecret.Spec.Push.Secret.Template == nil || (infisicalPushSecret.Spec.Push.Secret.Template != nil && infisicalPushSecret.Spec.Push.Secret.Template.IncludeAllSecrets) {
|
||||||
|
for key, value := range kubePushSecret.Data {
|
||||||
|
processedSecrets[key] = string(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalPushSecret.Spec.Push.Secret.Template != nil &&
|
||||||
|
len(infisicalPushSecret.Spec.Push.Secret.Template.Data) > 0 {
|
||||||
|
|
||||||
|
for templateKey, userTemplate := range infisicalPushSecret.Spec.Push.Secret.Template.Data {
|
||||||
|
|
||||||
|
tmpl, err := template.New("push-secret-templates").Funcs(util.InfisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
err = tmpl.Execute(buf, sourceSecrets)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
processedSecrets[templateKey] = buf.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedSecrets, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret v1alpha1.InfisicalPushSecret) error {
|
func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret v1alpha1.InfisicalPushSecret) error {
|
||||||
|
|
||||||
resourceVariables := r.getResourceVariables(infisicalPushSecret)
|
resourceVariables := r.getResourceVariables(infisicalPushSecret)
|
||||||
@@ -134,10 +180,9 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
|
return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var kubeSecrets = make(map[string]string)
|
processedSecrets, err := r.processTemplatedSecrets(infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination)
|
||||||
|
if err != nil {
|
||||||
for key, value := range kubePushSecret.Data {
|
return fmt.Errorf("unable to process templated secrets [err=%s]", err)
|
||||||
kubeSecrets[key] = string(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destination := infisicalPushSecret.Spec.Destination
|
destination := infisicalPushSecret.Spec.Destination
|
||||||
@@ -191,7 +236,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
|
|
||||||
infisicalPushSecret.Status.ManagedSecrets = make(map[string]string) // (string[id], string[key] )
|
infisicalPushSecret.Status.ManagedSecrets = make(map[string]string) // (string[id], string[key] )
|
||||||
|
|
||||||
for secretKey, secretValue := range kubeSecrets {
|
for secretKey, secretValue := range processedSecrets {
|
||||||
if exists := getExistingSecretByKey(secretKey); exists != nil {
|
if exists := getExistingSecretByKey(secretKey); exists != nil {
|
||||||
|
|
||||||
if updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
|
if updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
|
||||||
@@ -280,7 +325,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
// We need to check if any of the secrets have been removed in the new kube secret
|
// We need to check if any of the secrets have been removed in the new kube secret
|
||||||
for _, managedSecretKey := range infisicalPushSecret.Status.ManagedSecrets {
|
for _, managedSecretKey := range infisicalPushSecret.Status.ManagedSecrets {
|
||||||
|
|
||||||
if _, ok := kubeSecrets[managedSecretKey]; !ok {
|
if _, ok := processedSecrets[managedSecretKey]; !ok {
|
||||||
|
|
||||||
// Secret has been removed, verify that the secret is managed by the operator
|
// Secret has been removed, verify that the secret is managed by the operator
|
||||||
if getExistingSecretByKey(managedSecretKey) != nil {
|
if getExistingSecretByKey(managedSecretKey) != nil {
|
||||||
@@ -305,7 +350,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need to check if any new secrets have been added in the kube secret
|
// We need to check if any new secrets have been added in the kube secret
|
||||||
for currentSecretKey := range kubeSecrets {
|
for currentSecretKey := range processedSecrets {
|
||||||
|
|
||||||
if exists := getExistingSecretByKey(currentSecretKey); exists == nil {
|
if exists := getExistingSecretByKey(currentSecretKey); exists == nil {
|
||||||
|
|
||||||
@@ -317,7 +362,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
|
|
||||||
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
|
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
|
||||||
SecretKey: currentSecretKey,
|
SecretKey: currentSecretKey,
|
||||||
SecretValue: kubeSecrets[currentSecretKey],
|
SecretValue: processedSecrets[currentSecretKey],
|
||||||
ProjectID: destination.ProjectID,
|
ProjectID: destination.ProjectID,
|
||||||
Environment: destination.EnvironmentSlug,
|
Environment: destination.EnvironmentSlug,
|
||||||
SecretPath: destination.SecretsPath,
|
SecretPath: destination.SecretsPath,
|
||||||
@@ -336,12 +381,12 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
|
|
||||||
existingSecret := getExistingSecretByKey(currentSecretKey)
|
existingSecret := getExistingSecretByKey(currentSecretKey)
|
||||||
|
|
||||||
if existingSecret != nil && existingSecret.SecretValue != kubeSecrets[currentSecretKey] {
|
if existingSecret != nil && existingSecret.SecretValue != processedSecrets[currentSecretKey] {
|
||||||
logger.Info(fmt.Sprintf("Secret with key [key=%s] has changed value. Updating secret in Infisical", currentSecretKey))
|
logger.Info(fmt.Sprintf("Secret with key [key=%s] has changed value. Updating secret in Infisical", currentSecretKey))
|
||||||
|
|
||||||
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
|
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
|
||||||
SecretKey: currentSecretKey,
|
SecretKey: currentSecretKey,
|
||||||
NewSecretValue: kubeSecrets[currentSecretKey],
|
NewSecretValue: processedSecrets[currentSecretKey],
|
||||||
ProjectID: destination.ProjectID,
|
ProjectID: destination.ProjectID,
|
||||||
Environment: destination.EnvironmentSlug,
|
Environment: destination.EnvironmentSlug,
|
||||||
SecretPath: destination.SecretsPath,
|
SecretPath: destination.SecretsPath,
|
||||||
@@ -353,7 +398,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
updateExistingSecretByKey(currentSecretKey, kubeSecrets[currentSecretKey])
|
updateExistingSecretByKey(currentSecretKey, processedSecrets[currentSecretKey])
|
||||||
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = currentSecretKey
|
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = currentSecretKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +406,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if any of the existing secrets values have changed
|
// Check if any of the existing secrets values have changed
|
||||||
for secretKey, secretValue := range kubeSecrets {
|
for secretKey, secretValue := range processedSecrets {
|
||||||
|
|
||||||
existingSecret := getExistingSecretByKey(secretKey)
|
existingSecret := getExistingSecretByKey(secretKey)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -156,16 +155,6 @@ func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKub
|
|||||||
return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil
|
return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var infisicalSecretTemplateFunctions = template.FuncMap{
|
|
||||||
"decodeBase64ToBytes": func(encodedString string) string {
|
|
||||||
decoded, err := base64.StdEncoding.DecodeString(encodedString)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Error: %v", err))
|
|
||||||
}
|
|
||||||
return string(decoded)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string {
|
func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string {
|
||||||
stringMap := make(map[string]string)
|
stringMap := make(map[string]string)
|
||||||
for k, v := range binaryMap {
|
for k, v := range binaryMap {
|
||||||
@@ -177,7 +166,7 @@ func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string {
|
|||||||
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReferenceInterface interface{}, secretsFromAPI []model.SingleEnvironmentVariable, ETag string, resourceType constants.ManagedKubeResourceType) error {
|
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReferenceInterface interface{}, secretsFromAPI []model.SingleEnvironmentVariable, ETag string, resourceType constants.ManagedKubeResourceType) error {
|
||||||
plainProcessedSecrets := make(map[string][]byte)
|
plainProcessedSecrets := make(map[string][]byte)
|
||||||
|
|
||||||
var managedTemplateData *v1alpha1.InfisicalSecretTemplate
|
var managedTemplateData *v1alpha1.SecretTemplate
|
||||||
|
|
||||||
if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET {
|
if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET {
|
||||||
managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template
|
managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template
|
||||||
@@ -201,7 +190,7 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
for templateKey, userTemplate := range managedTemplateData.Data {
|
for templateKey, userTemplate := range managedTemplateData.Data {
|
||||||
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
|
tmpl, err := template.New("secret-templates").Funcs(util.InfisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||||
}
|
}
|
||||||
@@ -322,7 +311,7 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
for templateKey, userTemplate := range managedTemplateData.Data {
|
for templateKey, userTemplate := range managedTemplateData.Data {
|
||||||
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
|
tmpl, err := template.New("secret-templates").Funcs(util.InfisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||||
}
|
}
|
||||||
@@ -373,7 +362,7 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedConfigMap(ctx context.
|
|||||||
}
|
}
|
||||||
|
|
||||||
for templateKey, userTemplate := range managedTemplateData.Data {
|
for templateKey, userTemplate := range managedTemplateData.Data {
|
||||||
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
|
tmpl, err := template.New("secret-templates").Funcs(util.InfisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||||
}
|
}
|
||||||
|
|||||||
20
k8-operator/packages/util/template.go
Normal file
20
k8-operator/packages/util/template.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
var InfisicalSecretTemplateFunctions = template.FuncMap{
|
||||||
|
"decodeBase64ToBytes": func(encodedString string) string {
|
||||||
|
decoded, err := base64.StdEncoding.DecodeString(encodedString)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Error: %v", err))
|
||||||
|
}
|
||||||
|
return string(decoded)
|
||||||
|
},
|
||||||
|
"encodeBase64": func(plainString string) string {
|
||||||
|
return base64.StdEncoding.EncodeToString([]byte(plainString))
|
||||||
|
},
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user