diff --git a/.infisicalignore b/.infisicalignore
index 8072078d42..b80ecaad50 100644
--- a/.infisicalignore
+++ b/.infisicalignore
@@ -12,3 +12,5 @@ docs/cli/commands/bootstrap.mdx:jwt:86
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:102
docs/self-hosting/guides/automated-bootstrapping.mdx:jwt:74
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx:generic-api-key:72
+k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml:private-key:11
+k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml:private-key:52
diff --git a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
index 67fc7f2a8a..0664f0cd87 100644
--- a/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
+++ b/docs/integrations/platforms/kubernetes/infisical-push-secret-crd.mdx
@@ -401,6 +401,59 @@ After applying the InfisicalPushSecret CRD, you should notice that the secrets y
+## 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.
+
+
+
+ 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.
+
+
+ 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
+ Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
+
+
## Applying the InfisicalPushSecret CRD to your cluster
Once you have configured the `InfisicalPushSecret` CRD with the required fields, you can apply it to your cluster.
diff --git a/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx b/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx
index 3266fd2b06..f23eb010db 100644
--- a/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx
+++ b/docs/integrations/platforms/kubernetes/infisical-secret-crd.mdx
@@ -654,30 +654,7 @@ To help transform your secrets further, the operator provides a set of built-in
### Available templating functions
-
- **Function name**: decodeBase64ToBytes
-
-**Description**:
-Given a base64 encoded string, this function will decodes the base64-encoded string.
-This function is useful when your secrets are already stored as base64 encoded value in Infisical.
-
-**Returns**: The decoded base64 string as bytes.
-
-**Example**:
-The example below assumes that the `BINARY_KEY_BASE64` secret is stored as a base64 encoded value in Infisical.
-The resulting managed secret will contain the decoded value of `BINARY_KEY_BASE64`.
-
-```yaml
-managedKubeSecretReferences:
-secretName: managed-secret
-secretNamespace: default
-template:
- includeAllSecrets: true
- data:
- BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
-```
-
-
+Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
@@ -783,31 +760,8 @@ Using Go templates, you can format, combine, and create new key-value pairs from
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
-
-
- **Function name**: decodeBase64ToBytes
-
- **Description**:
- Given a base64 encoded string, this function will decodes the base64-encoded string.
- This function is useful when your Infisical secrets are already stored as base64 encoded value in Infisical.
-
- **Returns**: The decoded base64 string as bytes.
-
- **Example**:
- The example below assumes that the `BINARY_KEY_BASE64` secret is stored as a base64 encoded value in Infisical.
- The resulting managed config map will contain the decoded value of `BINARY_KEY_BASE64`.
-
- ```yaml
- managedKubeConfigMapReferences:
- - configMapName: managed-configmap
- configMapNamespace: default
- template:
- includeAllSecrets: true
- data:
- BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
- ```
-
-
+
+ Please refer to the [templating functions documentation](/integrations/platforms/kubernetes/overview#available-helper-functions) for more information.
## Applying CRD
@@ -854,39 +808,39 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
This will take all the secrets from your managed secret and expose them to your container
-````yaml
- envFrom:
- - secretRef:
- name: managed-secret # managed secret name
- ```
+ ````yaml
+ envFrom:
+ - secretRef:
+ name: managed-secret # managed secret name
+ ```
- Example usage in a deployment
- ```yaml
- apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ Example usage in a deployment
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- envFrom:
- - secretRef:
- name: managed-secret # <- name of managed secret
- ports:
- - containerPort: 80
-````
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ envFrom:
+ - secretRef:
+ name: managed-secret # <- name of managed secret
+ ports:
+ - containerPort: 80
+ ````
@@ -902,91 +856,90 @@ spec:
key: SOME_SECRET_KEY # The name of the key which exists in the managed secret
```
-Example usage in a deployment
+ Example usage in a deployment
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- env:
- - name: STRIPE_API_SECRET
- valueFrom:
- secretKeyRef:
- name: managed-secret # <- name of managed secret
- key: STRIPE_API_SECRET
- ports:
- - containerPort: 80
-```
-
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ env:
+ - name: STRIPE_API_SECRET
+ valueFrom:
+ secretKeyRef:
+ name: managed-secret # <- name of managed secret
+ key: STRIPE_API_SECRET
+ ports:
+ - containerPort: 80
+ ```
-This will allow you to create a volume on your container which comprises of files holding the secrets in your managed kubernetes secret
-```yaml
-volumes:
- - name: secrets-volume-name # The name of the volume under which secrets will be stored
- secret:
- secretName: managed-secret # managed secret name
-````
+ This will allow you to create a volume on your container which comprises of files holding the secrets in your managed kubernetes secret
+ ```yaml
+ volumes:
+ - name: secrets-volume-name # The name of the volume under which secrets will be stored
+ secret:
+ secretName: managed-secret # managed secret name
+ ````
-You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
+ You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
-```yaml
-volumeMounts:
- - name: secrets-volume-name
- mountPath: /etc/secrets
- readOnly: true
-```
+ ```yaml
+ volumeMounts:
+ - name: secrets-volume-name
+ mountPath: /etc/secrets
+ readOnly: true
+ ```
-Example usage in a deployment
+ Example usage in a deployment
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- volumeMounts:
- - name: secrets-volume-name
- mountPath: /etc/secrets
- readOnly: true
- ports:
- - containerPort: 80
- volumes:
- - name: secrets-volume-name
- secret:
- secretName: managed-secret # <- managed secrets
-```
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ volumeMounts:
+ - name: secrets-volume-name
+ mountPath: /etc/secrets
+ readOnly: true
+ ports:
+ - containerPort: 80
+ volumes:
+ - name: secrets-volume-name
+ secret:
+ secretName: managed-secret # <- managed secrets
+ ```
@@ -1021,34 +974,34 @@ secrets.infisical.com/auto-reload: "true"
```
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
- annotations:
- secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
-spec:
- replicas: 1
- selector:
- matchLabels:
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ annotations:
+ secrets.infisical.com/auto-reload: "true" # <- redeployment annotation
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- envFrom:
- - secretRef:
- name: managed-secret
- ports:
- - containerPort: 80
-```
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ envFrom:
+ - secretRef:
+ name: managed-secret
+ ports:
+ - containerPort: 80
+ ```
#### How it works
@@ -1069,39 +1022,39 @@ Here, we will highlight three of the most common ways to utilize it. Learn more
This will take all the secrets from your managed ConfigMap and expose them to your container
-````yaml
- envFrom:
- - configMapRef:
- name: managed-configmap # managed configmap name
- ```
+ ````yaml
+ envFrom:
+ - configMapRef:
+ name: managed-configmap # managed configmap name
+ ```
- Example usage in a deployment
- ```yaml
- apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ Example usage in a deployment
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- envFrom:
- - configMapRef:
- name: managed-configmap # <- name of managed configmap
- ports:
- - containerPort: 80
-````
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ envFrom:
+ - configMapRef:
+ name: managed-configmap # <- name of managed configmap
+ ports:
+ - containerPort: 80
+ ````
@@ -1117,92 +1070,91 @@ spec:
key: SOME_CONFIG_KEY # The name of the key which exists in the managed configmap
```
-Example usage in a deployment
+ Example usage in a deployment
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- env:
- - name: STRIPE_API_SECRET
- valueFrom:
- configMapKeyRef:
- name: managed-configmap # <- name of managed configmap
- key: STRIPE_API_SECRET
- ports:
- - containerPort: 80
-```
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ env:
+ - name: STRIPE_API_SECRET
+ valueFrom:
+ configMapKeyRef:
+ name: managed-configmap # <- name of managed configmap
+ key: STRIPE_API_SECRET
+ ports:
+ - containerPort: 80
+ ```
-This will allow you to create a volume on your container which comprises of files holding the secrets in your managed kubernetes secret
-```yaml
-volumes:
- - name: configmaps-volume-name # The name of the volume under which configmaps will be stored
- configMap:
- name: managed-configmap # managed configmap name
-````
+ This will allow you to create a volume on your container which comprises of files holding the secrets in your managed kubernetes secret
+ ```yaml
+ volumes:
+ - name: configmaps-volume-name # The name of the volume under which configmaps will be stored
+ configMap:
+ name: managed-configmap # managed configmap name
+ ````
-You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
+ You can then mount this volume to the container's filesystem so that your deployment can access the files containing the managed secrets
-```yaml
-volumeMounts:
- - name: configmaps-volume-name
- mountPath: /etc/config
- readOnly: true
-```
+ ```yaml
+ volumeMounts:
+ - name: configmaps-volume-name
+ mountPath: /etc/config
+ readOnly: true
+ ```
-Example usage in a deployment
+ Example usage in a deployment
-```yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx-deployment
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
+ ```yaml
+ apiVersion: apps/v1
+ kind: Deployment
+ metadata:
+ name: nginx-deployment
+ labels:
app: nginx
- template:
- metadata:
- labels:
+ spec:
+ replicas: 1
+ selector:
+ matchLabels:
app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:1.14.2
- volumeMounts:
- - name: configmaps-volume-name
- mountPath: /etc/config
- readOnly: true
- ports:
- - containerPort: 80
- volumes:
- - name: configmaps-volume-name
- configMap:
- name: managed-configmap # <- managed configmap
-```
-
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.14.2
+ volumeMounts:
+ - name: configmaps-volume-name
+ mountPath: /etc/config
+ readOnly: true
+ ports:
+ - containerPort: 80
+ volumes:
+ - name: configmaps-volume-name
+ configMap:
+ name: managed-configmap # <- managed configmap
+ ```
The definition file of the Kubernetes secret for the CA certificate can be structured like the following:
@@ -1228,37 +1180,37 @@ The operator will transfer all labels & annotations present on the `InfisicalSec
Thus, if a specific label is required on the resulting secret, it can be applied as demonstrated in the following example:
-```yaml
-apiVersion: secrets.infisical.com/v1alpha1
-kind: InfisicalSecret
-metadata:
- name: infisicalsecret-sample
- labels:
- label-to-be-passed-to-managed-secret: sample-value
- annotations:
- example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
-spec:
- ..
- authentication:
- ...
- managedKubeSecretReferences:
- ...
-```
+ ```yaml
+ apiVersion: secrets.infisical.com/v1alpha1
+ kind: InfisicalSecret
+ metadata:
+ name: infisicalsecret-sample
+ labels:
+ label-to-be-passed-to-managed-secret: sample-value
+ annotations:
+ example.com/annotation-to-be-passed-to-managed-secret: "sample-value"
+ spec:
+ ..
+ authentication:
+ ...
+ managedKubeSecretReferences:
+ ...
+ ```
-This would result in the following managed secret to be created:
+ This would result in the following managed secret to be created:
-```yaml
-apiVersion: v1
-data: ...
-kind: Secret
-metadata:
- annotations:
- example.com/annotation-to-be-passed-to-managed-secret: sample-value
- secrets.infisical.com/version: W/"3f1-ZyOSsrCLGSkAhhCkY2USPu2ivRw"
- labels:
- label-to-be-passed-to-managed-secret: sample-value
- name: managed-token
- namespace: default
-type: Opaque
-```
+ ```yaml
+ apiVersion: v1
+ data: ...
+ kind: Secret
+ metadata:
+ annotations:
+ example.com/annotation-to-be-passed-to-managed-secret: sample-value
+ secrets.infisical.com/version: W/"3f1-ZyOSsrCLGSkAhhCkY2USPu2ivRw"
+ labels:
+ label-to-be-passed-to-managed-secret: sample-value
+ name: managed-token
+ namespace: default
+ type: Opaque
+ ```
diff --git a/docs/integrations/platforms/kubernetes/overview.mdx b/docs/integrations/platforms/kubernetes/overview.mdx
index c4e3f7c642..ca700d7775 100644
--- a/docs/integrations/platforms/kubernetes/overview.mdx
+++ b/docs/integrations/platforms/kubernetes/overview.mdx
@@ -114,6 +114,48 @@ spec:
```
+## Advanced Templating
+
+With the Infisical Secrets Operator, you can use templating to dynamically generate secrets in Kubernetes. The templating is built on top of [Go templates](https://pkg.go.dev/text/template), which is a powerful and flexible template engine built into Go.
+
+Please be aware that trying to reference non-existing keys will result in an error. Additionally, each template field is processed individually, which means one template field cannot reference another template field.
+
+
+ Please note that templating is currently only supported for the `InfisicalPushSecret` and `InfisicalSecret` CRDs.
+
+
+### Available helper functions
+
+The Infisical Secrets Operator exposes a wide range of helper functions to make it easier to work with secrets in Kubernetes.
+
+| Function | Description | Signature |
+| -------- | ----------- | --------- |
+| `decodeBase64ToBytes` | Given a base64 encoded string, this function will decode the base64-encoded string. | `decodeBase64ToBytes(encodedString string) string` |
+| `encodeBase64` | Given a string, this function will encode the string to a base64 encoded string. | `encodeBase64(plainString string) string` |
+| `pkcs12key`| Extracts all private keys from a PKCS#12 archive and encodes them in PKCS#8 PEM format. | `pkcs12key(input string) string` |
+| `pkcs12keyPass`|Same as pkcs12key. Uses the provided password to decrypt the PKCS#12 archive. | `pkcs12keyPass(pass string, input string) string` |
+| `pkcs12cert` | Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is. Sort order: `leaf / intermediate(s) / root`. | `pkcs12cert(input string) string` |
+| `pkcs12certPass` | Same as `pkcs12cert`. Uses the provided password to decrypt the PKCS#12 archive. | `pkcs12certPass(pass string, input string) string` |
+| `pemToPkcs12` | Takes a PEM encoded certificate and key and creates a base64 encoded PKCS#12 archive. | `pemToPkcs12(cert string, key string) string` |
+| `pemToPkcs12Pass` | Same as `pemToPkcs12`. Uses the provided password to encrypt the PKCS#12 archive. | `pemToPkcs12Pass(cert string, key string, pass string) string` |
+| `fullPemToPkcs12` | Takes a PEM encoded certificates chain and key and creates a base64 encoded PKCS#12 archive. | `fullPemToPkcs12(cert string, key string) string` |
+| `fullPemToPkcs12Pass` | Same as `fullPemToPkcs12`. Uses the provided password to encrypt the PKCS#12 archive. | `fullPemToPkcs12Pass(cert string, key string, pass string) string` |
+| `filterPEM` | Filters PEM blocks with a specific type from a list of PEM blocks.. | `filterPEM(pemType string, input string) string` |
+| `filterCertChain` | Filters PEM block(s) with a specific certificate type (`leaf`, `intermediate` or `root`) from a certificate chain of PEM blocks (PEM blocks with type `CERTIFICATE`). | `filterCertChain(certType string, input string) string` |
+| `jwkPublicKeyPem` | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details. | `jwkPublicKeyPem(jwkjson string) string` |
+| `jwkPrivateKeyPem` | Takes an json-serialized JWK and returns an PEM block of type `PRIVATE KEY` that contains the private key. [See here](https://pkg.go.dev/crypto/x509#MarshalPKCS8PrivateKey) for details. | `jwkPrivateKeyPem(jwkjson string) string` |
+| `toYaml` | Takes an interface, marshals it to yaml. It returns a string, even on marshal error (empty string). | `toYaml(v any) string` |
+| `fromYaml` | Function converts a YAML document into a `map[string]any`. | `fromYaml(str string) map[string]any` |
+
+### Sprig functions
+
+The Infisical Secrets Operator integrates with the [Sprig library](https://github.com/Masterminds/sprig) to provide additional helper functions.
+
+
+ We've removed `expandEnv` and `env` from the supported functions for security reasons.
+
+
+
## Global configuration
To configure global settings that will apply to all instances of `InfisicalSecret`, you can define these configurations in a Kubernetes ConfigMap.
diff --git a/k8-operator/api/v1alpha1/common.go b/k8-operator/api/v1alpha1/common.go
index 5631984d10..2489b0b95d 100644
--- a/k8-operator/api/v1alpha1/common.go
+++ b/k8-operator/api/v1alpha1/common.go
@@ -105,7 +105,7 @@ type ManagedKubeSecretConfig struct {
// The template to transform the secret data
// +kubebuilder:validation:Optional
- Template *InfisicalSecretTemplate `json:"template,omitempty"`
+ Template *SecretTemplate `json:"template,omitempty"`
}
type ManagedKubeConfigMapConfig struct {
@@ -127,5 +127,15 @@ type ManagedKubeConfigMapConfig struct {
// The template to transform the secret data
// +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"`
}
diff --git a/k8-operator/api/v1alpha1/infisicalpushsecret_types.go b/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
index 9a1040868c..5a74388816 100644
--- a/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
+++ b/k8-operator/api/v1alpha1/infisicalpushsecret_types.go
@@ -16,9 +16,22 @@ type InfisicalPushSecretDestination struct {
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 {
// +kubebuilder:validation:Required
- Secret KubeSecretReference `json:"secret"`
+ Secret InfisicalPushSecretSecretSource `json:"secret"`
}
// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret
diff --git a/k8-operator/api/v1alpha1/infisicalsecret_types.go b/k8-operator/api/v1alpha1/infisicalsecret_types.go
index 8f871dfa6f..e90c06938d 100644
--- a/k8-operator/api/v1alpha1/infisicalsecret_types.go
+++ b/k8-operator/api/v1alpha1/infisicalsecret_types.go
@@ -116,16 +116,6 @@ type MachineIdentityScopeInWorkspace struct {
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
type InfisicalSecretSpec struct {
// +kubebuilder:validation:Optional
diff --git a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
index 11d864ad4f..2ad97108d4 100644
--- a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
@@ -383,7 +383,7 @@ func (in *InfisicalPushSecret) DeepCopyInto(out *InfisicalPushSecret) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
- out.Spec = in.Spec
+ in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
@@ -452,12 +452,32 @@ func (in *InfisicalPushSecretList) DeepCopyObject() runtime.Object {
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.
func (in *InfisicalPushSecretSpec) DeepCopyInto(out *InfisicalPushSecretSpec) {
*out = *in
out.Destination = in.Destination
out.Authentication = in.Authentication
- out.Push = in.Push
+ in.Push.DeepCopyInto(&out.Push)
out.TLS = in.TLS
}
@@ -614,28 +634,6 @@ func (in *InfisicalSecretStatus) DeepCopy() *InfisicalSecretStatus {
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.
func (in *KubeSecretReference) DeepCopyInto(out *KubeSecretReference) {
*out = *in
@@ -703,7 +701,7 @@ func (in *ManagedKubeConfigMapConfig) DeepCopyInto(out *ManagedKubeConfigMapConf
*out = *in
if in.Template != nil {
in, out := &in.Template, &out.Template
- *out = new(InfisicalSecretTemplate)
+ *out = new(SecretTemplate)
(*in).DeepCopyInto(*out)
}
}
@@ -723,7 +721,7 @@ func (in *ManagedKubeSecretConfig) DeepCopyInto(out *ManagedKubeSecretConfig) {
*out = *in
if in.Template != nil {
in, out := &in.Template, &out.Template
- *out = new(InfisicalSecretTemplate)
+ *out = new(SecretTemplate)
(*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.
func (in *SecretPush) DeepCopyInto(out *SecretPush) {
*out = *in
- out.Secret = in.Secret
+ in.Secret.DeepCopyInto(&out.Secret)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretPush.
@@ -769,6 +767,28 @@ func (in *SecretScopeInWorkspace) DeepCopy() *SecretScopeInWorkspace {
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.
func (in *ServiceAccountDetails) DeepCopyInto(out *ServiceAccountDetails) {
*out = *in
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
index a12ec9dbe7..25fafd98a5 100644
--- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml
@@ -137,6 +137,19 @@ spec:
description: The name space where the Kubernetes Secret is
located
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:
- secretName
- secretNamespace
diff --git a/k8-operator/config/samples/crd/infisicalsecret/infisical-secret-crd-with-template.yml b/k8-operator/config/samples/crd/infisicalsecret/infisical-secret-crd-with-template.yml
index c1e0c86b9b..06086e93f8 100644
--- a/k8-operator/config/samples/crd/infisicalsecret/infisical-secret-crd-with-template.yml
+++ b/k8-operator/config/samples/crd/infisicalsecret/infisical-secret-crd-with-template.yml
@@ -41,6 +41,9 @@ spec:
# Native Kubernetes Auth
kubernetesAuth:
+ serviceAccountRef:
+ name:
+ namespace:
identityId:
serviceAccountTokenPath: "/path/to/your/service-account/token" # Optional, defaults to /var/run/secrets/kubernetes.io/serviceaccount/token
@@ -97,18 +100,13 @@ spec:
secretsPath: "/path"
recursive: true
- managedSecretReference:
- secretName: managed-secret
- secretNamespace: default
- template:
- includeAllSecrets: true
- data:
- SSH_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
- BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
- creationPolicy: "Orphan" ## Owner | Orphan
- # secretType: kubernetes.io/dockerconfigjson
-
- # # To be depreciated soon
- # tokenSecretReference:
- # secretName: service-token
- # secretNamespace: default
+ managedKubeSecretReferences:
+ - secretName: managed-secret
+ secretNamespace: default
+ creationPolicy: "Orphan" ## Owner | Orphan
+ # secretType: kubernetes.io/dockerconfigjson
+ template:
+ includeAllSecrets: true
+ data:
+ SSH_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
+ BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
diff --git a/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml b/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml
index 108b86e783..82bb8039c7 100644
--- a/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml
+++ b/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml
@@ -41,6 +41,9 @@ spec:
# Native Kubernetes Auth
kubernetesAuth:
+ serviceAccountRef:
+ name:
+ namespace:
identityId:
serviceAccountTokenPath: "/path/to/your/service-account/token" # Optional, defaults to /var/run/secrets/kubernetes.io/serviceaccount/token
@@ -97,7 +100,7 @@ spec:
secretsPath: "/path"
recursive: true
- managedSecretReferences:
+ managedKubeSecretReferences:
- secretName: managed-secret
secretNamespace: default
creationPolicy: "Orphan" ## Owner | Orphan
diff --git a/k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml b/k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml
new file mode 100644
index 0000000000..a81c2189da
--- /dev/null
+++ b/k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml
@@ -0,0 +1,91 @@
+apiVersion: secrets.infisical.com/v1alpha1
+kind: InfisicalPushSecret
+metadata:
+ name: infisical-api-secret-sample-push
+spec:
+ resyncInterval: 1m
+ hostAPI: https://app.infisical.com/api # This is the default hostAPI for the Infisical 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:
+ environmentSlug:
+ secretsPath:
+
+ push:
+ secret:
+ secretName: push-secret-demo-with-templating
+ secretNamespace: default
+ template:
+ includeAllSecrets: false
+ data:
+ PKCS12_CERT_NO_PASSWORD: "{{ .PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert }}"
+ PKCS12_KEY_NO_PASSWORD: "{{ .PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key }}"
+
+ PKCS12_CERT_WITH_PASSWORD: '{{ .PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456" }}'
+ PKCS12_KEY_WITH_PASSWORD: '{{ .PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456" }}'
+
+ PEM_TO_PKCS12_PASS: '{{ pemToPkcs12Pass
+ (.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456")
+ (.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456")
+ "123456" }}'
+ PEM_TO_PKCS12_NO_PASSWORD: "{{ pemToPkcs12
+ (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert)
+ (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key)
+ }}"
+
+ FULL_PEM_TO_PKCS12_PASS: '{{ fullPemToPkcs12Pass
+ (.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12certPass "123456")
+ (.PKCS12_CONTENT_WITH_PASSWORD.Value | decodeBase64ToBytes | pkcs12keyPass "123456")
+ "123456" }}'
+ FULL_PEM_TO_PKCS12_NO_PASSWORD: "{{ fullPemToPkcs12
+ (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert)
+ (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12key)
+ }}"
+
+ FILTERED_PEM_CERT: '{{ filterPEM "CERTIFICATE" (printf "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----" .JWK_PRIVATE_RSA_PKCS8.Value .PKCS12_CONTENT_NO_PASSWORD.Value) }}'
+ FILTERED_PEM_KEY: '{{ filterPEM "PRIVATE KEY" (printf "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----\n-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----" .JWK_PRIVATE_RSA_PKCS8.Value .PKCS12_CONTENT_NO_PASSWORD.Value) }}'
+
+ # Will be empty with our current test data as there is no chain
+ CERT_CHAIN: '{{ filterCertChain "CERTIFICATE" (.PKCS12_CONTENT_NO_PASSWORD.Value | decodeBase64ToBytes | pkcs12cert) }}'
+
+ JWK_RSA_PUBLIC_PEM: "{{ jwkPublicKeyPem .JWK_PUB_RSA.Value }}"
+ JWK_ECDSA_PUBLIC_PEM: "{{ jwkPublicKeyPem .JWK_PUB_ECDSA.Value }}"
+
+ JWK_ECDSA_PRIVATE_PEM: "{{ jwkPrivateKeyPem .JWK_PRIV_ECDSA.Value }}"
+ JWK_RSA_PRIVATE_PEM: "{{ jwkPrivateKeyPem .JWK_PRIV_RSA.Value }}"
+
+ JSON_STR_TO_YAML: "{{ .TEST_JSON_DATA.Value | fromJsonStringToJson | toYaml }}"
+
+ FROM_YAML_TO_JSON: "{{ .TEST_YAML_STRING.Value | fromYaml | toJson }}"
+ YAML_ROUNDTRIP: "{{ .TEST_YAML_STRING.Value | fromYaml | toYaml }}"
+
+ TEST_LOWERCASE_STRING: "{{ .TEST_LOWERCASE_STRING.Value | upper }}"
+
+ # Only have one authentication method defined or you are likely to run into authentication issues.
+ # Remove all except one authentication method.
+ authentication:
+ awsIamAuth:
+ identityId:
+ azureAuth:
+ identityId:
+ gcpIamAuth:
+ identityId:
+ serviceAccountKeyFilePath:
+ gcpIdTokenAuth:
+ identityId:
+ kubernetesAuth:
+ identityId:
+ serviceAccountRef:
+ name:
+ namespace:
+ universalAuth:
+ credentialsRef:
+ secretName: # universal-auth-credentials
+ secretNamespace: # default
+
diff --git a/k8-operator/config/samples/crd/pushsecret/pushSecret.yaml b/k8-operator/config/samples/crd/pushsecret/push-secret.yaml
similarity index 96%
rename from k8-operator/config/samples/crd/pushsecret/pushSecret.yaml
rename to k8-operator/config/samples/crd/pushsecret/push-secret.yaml
index f1738fc474..b6a132987e 100644
--- a/k8-operator/config/samples/crd/pushsecret/pushSecret.yaml
+++ b/k8-operator/config/samples/crd/pushsecret/push-secret.yaml
@@ -19,7 +19,7 @@ spec:
push:
secret:
- secretName: push-secret-demo # Secret CRD
+ secretName: push-secret-source-secret # Secret CRD
secretNamespace: default
# Only have one authentication method defined or you are likely to run into authentication issues.
diff --git a/k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml b/k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml
new file mode 100644
index 0000000000..79a12a4d4d
--- /dev/null
+++ b/k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml
@@ -0,0 +1,91 @@
+# This is the source secret that you can use to demo the advanced templating functionality seen in `push-secret-with-template.yaml`
+
+apiVersion: v1
+kind: Secret
+metadata:
+ name: push-secret-demo-with-templating
+ namespace: default
+stringData:
+ PKCS12_CONTENT_NO_PASSWORD: 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
+ PKCS12_CONTENT_WITH_PASSWORD: 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
+ JWK_PRIVATE_RSA_PKCS8: "-----BEGIN PRIVATE KEY-----
+ MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCmN2yzxloN8Qfo
+ rpTsZ5bafEOpHgg/Tj1+TV8rSWd2KZswxUF0+/+FKmbxPwS0EPGtR2LU4dl8yFSL
+ EZq637edDgYb2czbj2jGEK3Gqo28ReuZBEapzPIvG6H58qf0WD76FL1SlrMel9UA
+ WcHloJ9eg2E+4jygHLIUowpo5WAc2o/k0ESppuIt+1kPdb+WwUI8a7OvhWnRhLvN
+ LaENhJwLag4y7isZTUtwxl/f2nfXncKrttLZeHpj6/DmnDMVhl2NDEOfzHwEbd8n
+ qPxMYtdCxsofXbXz8dxQlG8zB2ltRAbme8DYZdWoup3CnTngvOT38H9/WVWuY4q4
+ eNM0erjzAgMBAAECggEBAJLA5rnHTCV5BRmcYqJjR566DmcXvAJgywxjtb4bPjzm
+ uT2TO5rVD6J8cI1ZrYZqW2c5WvpIOeThXzu2HF4YPh5tjlkysJu9/6y4dyWr2h47
+ warFSrqK191d0WJEq6Oh8mCMxSdRJO7C8W4w0XAzo+Inr0l9KDfZfiWYWg2JT5XI
+ ubibKKq6P2KxND0UVlYbRsp3fv2loEL9WM5H2bjA/oSbQ4tSJtobpjlsQOHmaxbP
+ XhvsIV3Dr2ksDuLEhm0vfXnEGRzNk3HV3gLNT741YEP3Sp2ZRjd5U1qFn0D+eWe0
+ 4LfDX9auGQCnfjZTHvu4qghX7JxcF40omjmtgkRmZ/kCgYEA4A5nU4ahEww7B65y
+ uzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ++wwf
+ pRwHvSxtNU9qXb8ewo+BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3In
+ KF4JvIlchyqs0RQ8wx7lULqwnn0CgYEAven83GM6SfrmO+TBHbjTk6JhP/3CMsIv
+ mSdo4KrbQNvp4vHO3w1/0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEB
+ pxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA+k4UoH/eQmGKGK44TRz
+ Yj5hZYGWIC8CgYEAlmmU/AG5SGxBhJqb8wxfNXDPJjf//i92BgJT2Vp4pskBbr5P
+ GoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ+m0/XSWx13v9t9DIbheA
+ tgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpECgYEA
+ mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe//EjuCBbwHfcT8OG3hWOv8vpzo
+ kQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p+AF2p6Yfahscjtq+GY9cB85Nx
+ Ly2IXCC0PF++Sq9LOrTE9QV988SJy/yUrAjcZ5MmECkCgYEAldHXIrEmMZVaNwGz
+ DF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uY
+ iqewXfCKw/UngrJt8Xwfq1Zruz0YY869zPN4GiE9+9rzdZB33RBw8kIOquY3MK74
+ FMwCihYx/LiU2YTHkaoJ3ncvtvg=
+ -----END PRIVATE KEY-----"
+ JWK_PUB_RSA: '{"kid":"ex","kty":"RSA","key_ops":["sign","verify","wrapKey","unwrapKey","encrypt","decrypt"],"n":"p2VQo8qCfWAZmdWBVaYuYb-a-tWWm78K6Sr9poCvNcmv8rUPSLACxitQWR8gZaSH1DklVkqz-Ed8Cdlf8lkDg4Ex5tkB64jRdC1Uvn4CDpOH6cp-N2s8hTFLqy9_YaDmyQS7HiqthOi9oVjil1VMeWfaAbClGtFt6UnKD0Vb_DvLoWYQSqlhgBArFJi966b4E1pOq5Ad02K8pHBDThlIIx7unibLehhDU6q3DCwNH_OOLx6bgNtmvGYJDd1cywpkLQ3YzNCUPWnfMBJRP3iQP_WI21uP6cvo0DqBPBM4wvVzHbCT0vnIflwkbgEWkq1FprqAitZlop9KjLqzjp9vyQ","e":"AQAB"}'
+ JWK_PUB_ECDSA: '{"kid":"https://kv-test-mj.vault.azure.net/keys/ec-p-521/e3d0e9c179b54988860c69c6ae172c65","kty":"EC","key_ops":["sign","verify"],"crv":"P-521","x":"AedOAtb7H7Oz1C_cPKI_R4CN_eai5nteY6KFW07FOoaqgQfVCSkQDK22fCOiMT_28c8LZYJRsiIFz_IIbQUW7bXj","y":"AOnchHnmBphIWXvanmMAmcCDkaED6ycW8GsAl9fQ43BMVZTqcTkJYn6vGnhn7MObizmkNSmgZYTwG-vZkIg03HHs"}'
+
+ JWK_PRIV_RSA: '{"kty" : "RSA","kid" : "cc34c0a0-bd5a-4a3c-a50d-a2a7db7643df","use" : "sig","n" : "pjdss8ZaDfEH6K6U7GeW2nxDqR4IP049fk1fK0lndimbMMVBdPv_hSpm8T8EtBDxrUdi1OHZfMhUixGaut-3nQ4GG9nM249oxhCtxqqNvEXrmQRGqczyLxuh-fKn9Fg--hS9UpazHpfVAFnB5aCfXoNhPuI8oByyFKMKaOVgHNqP5NBEqabiLftZD3W_lsFCPGuzr4Vp0YS7zS2hDYScC2oOMu4rGU1LcMZf39p3153Cq7bS2Xh6Y-vw5pwzFYZdjQxDn8x8BG3fJ6j8TGLXQsbKH1218_HcUJRvMwdpbUQG5nvA2GXVqLqdwp054Lzk9_B_f1lVrmOKuHjTNHq48w","e" : "AQAB","d" : "ksDmucdMJXkFGZxiomNHnroOZxe8AmDLDGO1vhs-POa5PZM7mtUPonxwjVmthmpbZzla-kg55OFfO7YcXhg-Hm2OWTKwm73_rLh3JavaHjvBqsVKuorX3V3RYkSro6HyYIzFJ1Ek7sLxbjDRcDOj4ievSX0oN9l-JZhaDYlPlci5uJsoqro_YrE0PRRWVhtGynd-_aWgQv1YzkfZuMD-hJtDi1Im2humOWxA4eZrFs9eG-whXcOvaSwO4sSGbS99ecQZHM2TcdXeAs1PvjVgQ_dKnZlGN3lTWoWfQP55Z7Tgt8Nf1q4ZAKd-NlMe-7iqCFfsnFwXjSiaOa2CRGZn-Q","p" : "4A5nU4ahEww7B65yuzmGeCUUi8ikWzv1C81pSyUKvKzu8CX41hp9J6oRaLGesKImYiuVQK47FhZ--wwfpRwHvSxtNU9qXb8ewo-BvadyO1eVrIk4tNV543QlSe7pQAoJGkxCia5rfznAE3InKF4JvIlchyqs0RQ8wx7lULqwnn0","q" : "ven83GM6SfrmO-TBHbjTk6JhP_3CMsIvmSdo4KrbQNvp4vHO3w1_0zJ3URkmkYGhz2tgPlfd7v1l2I6QkIh4Bumdj6FyFZEBpxjE4MpfdNVcNINvVj87cLyTRmIcaGxmfylY7QErP8GFA-k4UoH_eQmGKGK44TRzYj5hZYGWIC8","dp" : "lmmU_AG5SGxBhJqb8wxfNXDPJjf__i92BgJT2Vp4pskBbr5PGoyV0HbfUQVMnw977RONEurkR6O6gxZUeCclGt4kQlGZ-m0_XSWx13v9t9DIbheAtgVJ2mQyVDvK4m7aRYlEceFh0PsX8vYDS5o1txgPwb3oXkPTtrmbAGMUBpE","dq" : "mxRTU3QDyR2EnCv0Nl0TCF90oliJGAHR9HJmBe__EjuCBbwHfcT8OG3hWOv8vpzokQPRl5cQt3NckzX3fs6xlJN4Ai2Hh2zduKFVQ2p-AF2p6Yfahscjtq-GY9cB85NxLy2IXCC0PF--Sq9LOrTE9QV988SJy_yUrAjcZ5MmECk","qi" : "ldHXIrEmMZVaNwGzDF9WG8sHj2mOZmQpw9yrjLK9hAsmsNr5LTyqWAqJIYZSwPTYWhY4nu2O0EY9G9uYiqewXfCKw_UngrJt8Xwfq1Zruz0YY869zPN4GiE9-9rzdZB33RBw8kIOquY3MK74FMwCihYx_LiU2YTHkaoJ3ncvtvg"}'
+ JWK_PRIV_ECDSA: '{"kty": "EC","kid": "rie3pHe8u8gjSa0IaJfqk7_iEfHeYfDYx-Bqi7vQc0s","crv": "P-256","x": "fDjg3Nq4jPf8IOZ0277aPVal_8iXySnzLUJAZghUzZM","y": "d863PeyBOK_Q4duiSmWwgIRzi1RPlFZTR-vACMlPg-Q","d": "jJs5xsoHUetdMabtt8H2KyX5T92nGul1chFeMT5hlr0"}'
+
+ TEST_LOWERCASE_STRING: i am a lowercase string
+
+ TEST_YAML_STRING: |
+ name: test-object
+ type: example
+ properties:
+ key1: value1
+ key2: value2
+ numbers:
+ - 1
+ - 2
+ - 3
+
+ TEST_JSON_DATA: '{
+ "id": "f1bc87fc-99cf-48d2-b289-7cf578152f9a",
+ "name": "test",
+ "description": "",
+ "type": "secret-manager",
+ "slug": "test-o-bto",
+ "autoCapitalization": false,
+ "orgId": "601815be-6884-4ee4-86c7-bfc6415f2123",
+ "createdAt": "2025-03-26T01:32:39.890Z",
+ "updatedAt": "2025-03-26T01:32:41.688Z",
+ "version": 3,
+ "upgradeStatus": null,
+ "pitVersionLimit": 10,
+ "kmsCertificateKeyId": null,
+ "auditLogsRetentionDays": null,
+ "_id": "f1bc87fc-99cf-48d2-b289-7cf578152f9a",
+ "environments": [
+ {
+ "name": "Development",
+ "slug": "dev",
+ "id": "cbb62f88-44cb-4c29-975a-871f8d7d303b"
+ },
+ {
+ "name": "Staging",
+ "slug": "staging",
+ "id": "c933a63d-418a-4d5c-a7d1-91b74d3ee2eb"
+ },
+ {
+ "name": "Production",
+ "slug": "prod",
+ "id": "0b70125e-47d5-46e8-a03e-a3105df05d37"
+ }
+ ]
+ }'
diff --git a/k8-operator/config/samples/crd/pushsecret/source-secret.yaml b/k8-operator/config/samples/crd/pushsecret/source-secret.yaml
new file mode 100644
index 0000000000..88fd3e0d26
--- /dev/null
+++ b/k8-operator/config/samples/crd/pushsecret/source-secret.yaml
@@ -0,0 +1,9 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: push-secret-source-secret
+ namespace: default
+stringData:
+ ENCRYPTION_KEY: secret-encryption-key
+ API_URL: https://example.com/api
+ REGION: us-east-1
diff --git a/k8-operator/config/samples/crd/pushsecret/sourceSecret.yaml b/k8-operator/config/samples/crd/pushsecret/sourceSecret.yaml
deleted file mode 100644
index 6a3703a956..0000000000
--- a/k8-operator/config/samples/crd/pushsecret/sourceSecret.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-apiVersion: v1
-kind: Secret
-metadata:
- name: push-secret-demo
- namespace: default
-stringData: # can also be "data", but needs to be base64 encoded
- API_KEY: some-api-key
- DATABASE_URL: postgres://127.0.0.1:5432
- ENCRYPTION_KEY: fabcc12-a22-facbaa4-11aa568aab
diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
index b87c5bcc26..a5526bd6cf 100644
--- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
+++ b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go
@@ -65,6 +65,8 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.
if err != nil {
if errors.IsNotFound(err) {
logger.Info("Infisical Push Secret CRD not found")
+ r.DeleteManagedSecrets(ctx, logger, infisicalPushSecretCRD)
+
return ctrl.Result{
Requeue: false,
}, nil
diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
index 47fc8e69b3..848df3c829 100644
--- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
+++ b/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go
@@ -1,16 +1,21 @@
package controllers
import (
+ "bytes"
"context"
"errors"
"fmt"
"strings"
+ tpl "text/template"
"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/model"
+ "github.com/Infisical/infisical/k8-operator/packages/template"
"github.com/Infisical/infisical/k8-operator/packages/util"
"github.com/go-logr/logr"
+ corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -101,6 +106,48 @@ func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSec
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 := tpl.New("push-secret-templates").Funcs(template.GetTemplateFunctions()).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 {
resourceVariables := r.getResourceVariables(infisicalPushSecret)
@@ -134,10 +181,9 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
return fmt.Errorf("unable to fetch kube secret [err=%s]", err)
}
- var kubeSecrets = make(map[string]string)
-
- for key, value := range kubePushSecret.Data {
- kubeSecrets[key] = string(value)
+ processedSecrets, err := r.processTemplatedSecrets(infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination)
+ if err != nil {
+ return fmt.Errorf("unable to process templated secrets [err=%s]", err)
}
destination := infisicalPushSecret.Spec.Destination
@@ -191,7 +237,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
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 updatePolicy == string(constants.PUSH_SECRET_REPLACE_POLICY_ENABLED) {
@@ -280,7 +326,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
// We need to check if any of the secrets have been removed in the new kube secret
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
if getExistingSecretByKey(managedSecretKey) != nil {
@@ -305,7 +351,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
}
// 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 {
@@ -317,7 +363,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
createdSecret, err := infisicalClient.Secrets().Create(infisicalSdk.CreateSecretOptions{
SecretKey: currentSecretKey,
- SecretValue: kubeSecrets[currentSecretKey],
+ SecretValue: processedSecrets[currentSecretKey],
ProjectID: destination.ProjectID,
Environment: destination.EnvironmentSlug,
SecretPath: destination.SecretsPath,
@@ -336,12 +382,12 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
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))
updatedSecret, err := infisicalClient.Secrets().Update(infisicalSdk.UpdateSecretOptions{
SecretKey: currentSecretKey,
- NewSecretValue: kubeSecrets[currentSecretKey],
+ NewSecretValue: processedSecrets[currentSecretKey],
ProjectID: destination.ProjectID,
Environment: destination.EnvironmentSlug,
SecretPath: destination.SecretsPath,
@@ -353,7 +399,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
continue
}
- updateExistingSecretByKey(currentSecretKey, kubeSecrets[currentSecretKey])
+ updateExistingSecretByKey(currentSecretKey, processedSecrets[currentSecretKey])
infisicalPushSecret.Status.ManagedSecrets[updatedSecret.ID] = currentSecretKey
}
}
@@ -361,7 +407,7 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context
}
// Check if any of the existing secrets values have changed
- for secretKey, secretValue := range kubeSecrets {
+ for secretKey, secretValue := range processedSecrets {
existingSecret := getExistingSecretByKey(secretKey)
@@ -440,9 +486,28 @@ func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context
resourceVariables := r.getResourceVariables(infisicalPushSecret)
infisicalClient := resourceVariables.InfisicalClient
+ cancelCtx := resourceVariables.CancelCtx
+ authDetails := resourceVariables.AuthDetails
+ var err error
+
+ if authDetails.AuthStrategy == "" {
+ logger.Info("No authentication strategy found. Attempting to authenticate")
+ authDetails, err = r.handleAuthentication(ctx, infisicalPushSecret, infisicalClient)
+ r.SetAuthenticatedStatusCondition(ctx, &infisicalPushSecret, err)
+
+ if err != nil {
+ return fmt.Errorf("unable to authenticate [err=%s]", err)
+ }
+
+ r.updateResourceVariables(infisicalPushSecret, util.ResourceVariables{
+ InfisicalClient: infisicalClient,
+ CancelCtx: cancelCtx,
+ AuthDetails: authDetails,
+ })
+ }
destination := infisicalPushSecret.Spec.Destination
- existingSecrets, err := infisicalClient.Secrets().List(infisicalSdk.ListSecretsOptions{
+ existingSecrets, err := resourceVariables.InfisicalClient.Secrets().List(infisicalSdk.ListSecretsOptions{
ProjectID: destination.ProjectID,
Environment: destination.EnvironmentSlug,
SecretPath: destination.SecretsPath,
diff --git a/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go b/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go
index eb41fe6e91..f2197b0ed1 100644
--- a/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go
+++ b/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go
@@ -3,17 +3,17 @@ package controllers
import (
"bytes"
"context"
- "encoding/base64"
"errors"
"fmt"
"strings"
- "text/template"
+ tpl "text/template"
"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/crypto"
"github.com/Infisical/infisical/k8-operator/packages/model"
+ "github.com/Infisical/infisical/k8-operator/packages/template"
"github.com/Infisical/infisical/k8-operator/packages/util"
"github.com/go-logr/logr"
@@ -156,16 +156,6 @@ func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKub
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 {
stringMap := make(map[string]string)
for k, v := range binaryMap {
@@ -177,7 +167,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 {
plainProcessedSecrets := make(map[string][]byte)
- var managedTemplateData *v1alpha1.InfisicalSecretTemplate
+ var managedTemplateData *v1alpha1.SecretTemplate
if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET {
managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template
@@ -201,7 +191,7 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx conte
}
for templateKey, userTemplate := range managedTemplateData.Data {
- tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
+ tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate)
if err != nil {
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
}
@@ -322,7 +312,7 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context
}
for templateKey, userTemplate := range managedTemplateData.Data {
- tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
+ tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate)
if err != nil {
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
}
@@ -373,7 +363,7 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedConfigMap(ctx context.
}
for templateKey, userTemplate := range managedTemplateData.Data {
- tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
+ tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate)
if err != nil {
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
}
diff --git a/k8-operator/go.mod b/k8-operator/go.mod
index 0731666fab..6ce68ed7fc 100644
--- a/k8-operator/go.mod
+++ b/k8-operator/go.mod
@@ -3,12 +3,15 @@ module github.com/Infisical/infisical/k8-operator
go 1.21
require (
+ github.com/Masterminds/sprig/v3 v3.3.0
github.com/infisical/go-sdk v0.4.4
+ github.com/lestrrat-go/jwx/v2 v2.1.4
github.com/onsi/ginkgo/v2 v2.6.0
github.com/onsi/gomega v1.24.1
k8s.io/apimachinery v0.26.1
k8s.io/client-go v0.26.1
sigs.k8s.io/controller-runtime v0.14.4
+ software.sslmate.com/src/go-pkcs12 v0.5.0
)
require (
@@ -16,6 +19,9 @@ require (
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
cloud.google.com/go/compute/metadata v0.4.0 // indirect
cloud.google.com/go/iam v1.1.11 // indirect
+ dario.cat/mergo v1.0.1 // indirect
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.24 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.24 // indirect
@@ -29,18 +35,31 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/goccy/go-json v0.10.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.5 // indirect
+ github.com/huandu/xstrings v1.5.0 // indirect
+ github.com/lestrrat-go/blackmagic v1.0.2 // indirect
+ github.com/lestrrat-go/httpcc v1.0.1 // indirect
+ github.com/lestrrat-go/httprc v1.0.6 // indirect
+ github.com/lestrrat-go/iter v1.0.2 // indirect
+ github.com/lestrrat-go/option v1.0.1 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/segmentio/asm v1.2.0 // indirect
+ github.com/shopspring/decimal v1.4.0 // indirect
+ github.com/spf13/cast v1.7.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
google.golang.org/api v0.188.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240708141625-4ad9e859172b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
@@ -84,18 +103,18 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
- golang.org/x/crypto v0.25.0
+ golang.org/x/crypto v0.32.0
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
- golang.org/x/sys v0.22.0 // indirect
- golang.org/x/term v0.22.0 // indirect
- golang.org/x/text v0.16.0 // indirect
+ golang.org/x/sys v0.29.0 // indirect
+ golang.org/x/term v0.28.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
+ gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.26.1
k8s.io/apiextensions-apiserver v0.26.1 // indirect
k8s.io/component-base v0.26.1 // indirect
diff --git a/k8-operator/go.sum b/k8-operator/go.sum
index 9037bc4a06..bcecfadc01 100644
--- a/k8-operator/go.sum
+++ b/k8-operator/go.sum
@@ -38,9 +38,17 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
+github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
+github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -92,6 +100,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@@ -106,6 +116,8 @@ github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJ
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -138,6 +150,8 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -214,6 +228,8 @@ github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBY
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -239,10 +255,24 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
+github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
+github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
+github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
+github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
+github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
+github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
+github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
+github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc=
+github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is=
+github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
+github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
@@ -250,6 +280,10 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -300,9 +334,17 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
+github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
+github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
@@ -319,8 +361,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -362,8 +404,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
-golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -458,8 +500,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -506,16 +548,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
-golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -527,8 +569,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -728,3 +770,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kF
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
+software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M=
+software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
diff --git a/k8-operator/main.go b/k8-operator/main.go
index 79df0d4d8e..d2f1905953 100644
--- a/k8-operator/main.go
+++ b/k8-operator/main.go
@@ -22,6 +22,7 @@ import (
infisicalDynamicSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicaldynamicsecret"
infisicalPushSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalpushsecret"
infisicalSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalsecret"
+ "github.com/Infisical/infisical/k8-operator/packages/template"
//+kubebuilder:scaffold:imports
)
@@ -86,6 +87,8 @@ func main() {
os.Exit(1)
}
+ template.InitializeTemplateFunctions()
+
if err = (&infisicalSecretController.InfisicalSecretReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
diff --git a/k8-operator/packages/template/base64.go b/k8-operator/packages/template/base64.go
new file mode 100644
index 0000000000..3fff06c867
--- /dev/null
+++ b/k8-operator/packages/template/base64.go
@@ -0,0 +1,18 @@
+package template
+
+import (
+ "encoding/base64"
+ "fmt"
+)
+
+func decodeBase64ToBytes(encodedString string) string {
+ decoded, err := base64.StdEncoding.DecodeString(encodedString)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+ return string(decoded)
+}
+
+func encodeBase64(plainString string) string {
+ return base64.StdEncoding.EncodeToString([]byte(plainString))
+}
diff --git a/k8-operator/packages/template/jwk.go b/k8-operator/packages/template/jwk.go
new file mode 100644
index 0000000000..8dbc8f3792
--- /dev/null
+++ b/k8-operator/packages/template/jwk.go
@@ -0,0 +1,43 @@
+package template
+
+import (
+ "crypto/x509"
+ "fmt"
+
+ "github.com/lestrrat-go/jwx/v2/jwk"
+)
+
+func jwkPublicKeyPem(jwkjson string) string {
+ k, err := jwk.ParseKey([]byte(jwkjson))
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
+ }
+ var rawkey any
+ err = k.Raw(&rawkey)
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
+ }
+ mpk, err := x509.MarshalPKIXPublicKey(rawkey)
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
+ }
+ return pemEncode(mpk, "PUBLIC KEY")
+}
+
+func jwkPrivateKeyPem(jwkjson string) string {
+ k, err := jwk.ParseKey([]byte(jwkjson))
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
+ }
+ var mpk []byte
+ var pk any
+ err = k.Raw(&pk)
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
+ }
+ mpk, err = x509.MarshalPKCS8PrivateKey(pk)
+ if err != nil {
+ panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
+ }
+ return pemEncode(mpk, "PRIVATE KEY")
+}
diff --git a/k8-operator/packages/template/pem.go b/k8-operator/packages/template/pem.go
new file mode 100644
index 0000000000..f37a9d576f
--- /dev/null
+++ b/k8-operator/packages/template/pem.go
@@ -0,0 +1,98 @@
+package template
+
+import (
+ "bytes"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "strings"
+)
+
+const (
+ errJunk = "error filtering pem: found junk"
+
+ certTypeLeaf = "leaf"
+ certTypeIntermediate = "intermediate"
+ certTypeRoot = "root"
+)
+
+func filterPEM(pemType, input string) string {
+ data := []byte(input)
+ var blocks []byte
+ var block *pem.Block
+ var rest []byte
+ for {
+ block, rest = pem.Decode(data)
+ data = rest
+
+ if block == nil {
+ break
+ }
+ if !strings.EqualFold(block.Type, pemType) {
+ continue
+ }
+
+ var buf bytes.Buffer
+ err := pem.Encode(&buf, block)
+ if err != nil {
+ panic(fmt.Sprintf("[filterPEM] Error: %v", err))
+ }
+ blocks = append(blocks, buf.Bytes()...)
+ }
+
+ if len(blocks) == 0 && len(rest) != 0 {
+ panic(fmt.Sprintf("[filterPEM] Error: %v", errJunk))
+ }
+
+ return string(blocks)
+}
+
+func filterCertChain(certType, input string) string {
+ ordered := fetchX509CertChains([]byte(input))
+
+ switch certType {
+ case certTypeLeaf:
+ cert := ordered[0]
+ if cert.AuthorityKeyId != nil && !bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
+ return pemEncode(ordered[0].Raw, pemTypeCertificate)
+ }
+ case certTypeIntermediate:
+ if len(ordered) < 2 {
+ return ""
+ }
+ var pemData []byte
+ for _, cert := range ordered[1:] {
+ if isRootCertificate(cert) {
+ break
+ }
+ b := &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: cert.Raw,
+ }
+ pemData = append(pemData, pem.EncodeToMemory(b)...)
+ }
+ return string(pemData)
+ case certTypeRoot:
+ cert := ordered[len(ordered)-1]
+ if isRootCertificate(cert) {
+ return pemEncode(cert.Raw, pemTypeCertificate)
+ }
+ }
+
+ return ""
+}
+
+func isRootCertificate(cert *x509.Certificate) bool {
+ return cert.AuthorityKeyId == nil || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId)
+}
+
+func pemEncode(thing []byte, kind string) string {
+ buf := bytes.NewBuffer(nil)
+ err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: thing})
+
+ if err != nil {
+ panic(fmt.Sprintf("[pemEncode] Error: %v", err))
+ }
+
+ return buf.String()
+}
diff --git a/k8-operator/packages/template/pem_chain.go b/k8-operator/packages/template/pem_chain.go
new file mode 100644
index 0000000000..00c4f5d3f7
--- /dev/null
+++ b/k8-operator/packages/template/pem_chain.go
@@ -0,0 +1,117 @@
+package template
+
+import (
+ "bytes"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+)
+
+const (
+ errNilCert = "certificate is nil"
+ errFoundDisjunctCert = "found multiple leaf or disjunct certificates"
+ errNoLeafFound = "no leaf certificate found"
+ errChainCycle = "constructing chain resulted in cycle"
+)
+
+type node struct {
+ cert *x509.Certificate
+ parent *node
+ isParent bool
+}
+
+func fetchX509CertChains(data []byte) []*x509.Certificate {
+ var newCertChain []*x509.Certificate
+ nodes := pemToNodes(data)
+
+ // at the end of this computation, the output will be a single linked list
+ // the tail of the list will be the root node (which has no parents)
+ // the head of the list will be the leaf node (whose parent will be intermediate certs)
+ // (head) leaf -> intermediates -> root (tail)
+ for i := range nodes {
+ for j := range nodes {
+ // ignore same node to prevent generating a cycle
+ if i == j {
+ continue
+ }
+ // if ith node AuthorityKeyId is same as jth node SubjectKeyId, jth node was used
+ // to sign the ith certificate
+ if bytes.Equal(nodes[i].cert.AuthorityKeyId, nodes[j].cert.SubjectKeyId) {
+ nodes[j].isParent = true
+ nodes[i].parent = nodes[j]
+ break
+ }
+ }
+ }
+
+ var foundLeaf bool
+ var leaf *node
+ for i := range nodes {
+ if !nodes[i].isParent {
+ if foundLeaf {
+ panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errFoundDisjunctCert))
+ }
+ // this is the leaf node as it's not a parent for any other node
+ leaf = nodes[i]
+ foundLeaf = true
+ }
+ }
+
+ if leaf == nil {
+ panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errNoLeafFound))
+ }
+
+ processedNodes := 0
+ // iterate through the directed list and append the nodes to new cert chain
+ for leaf != nil {
+ processedNodes++
+ // ensure we aren't stuck in a cyclic loop
+ if processedNodes > len(nodes) {
+ panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errChainCycle))
+ }
+ newCertChain = append(newCertChain, leaf.cert)
+ leaf = leaf.parent
+ }
+ return newCertChain
+}
+
+func fetchCertChains(data []byte) []byte {
+ var pemData []byte
+ newCertChain := fetchX509CertChains(data)
+
+ for _, cert := range newCertChain {
+ b := &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: cert.Raw,
+ }
+ pemData = append(pemData, pem.EncodeToMemory(b)...)
+ }
+ return pemData
+}
+
+func pemToNodes(data []byte) []*node {
+ nodes := make([]*node, 0)
+ for {
+ // decode pem to der first
+ block, rest := pem.Decode(data)
+ data = rest
+
+ if block == nil {
+ break
+ }
+ cert, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ panic(fmt.Sprintf("[pemToNodes] Error: %v", err))
+ }
+
+ if cert == nil {
+ panic(fmt.Sprintf("[pemToNodes] Error: %v", errNilCert))
+ }
+ nodes = append(nodes, &node{
+ cert: cert,
+ parent: nil,
+ isParent: false,
+ })
+ }
+ return nodes
+}
diff --git a/k8-operator/packages/template/pkcs12.go b/k8-operator/packages/template/pkcs12.go
new file mode 100644
index 0000000000..e6763fc46e
--- /dev/null
+++ b/k8-operator/packages/template/pkcs12.go
@@ -0,0 +1,144 @@
+package template
+
+import (
+ "bytes"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/pem"
+ "fmt"
+
+ gopkcs12 "software.sslmate.com/src/go-pkcs12"
+)
+
+func pkcs12keyPass(pass, input string) string {
+ privateKey, _, _, err := gopkcs12.DecodeChain([]byte(input), pass)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ marshalPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ var buf bytes.Buffer
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeKey,
+ Bytes: marshalPrivateKey,
+ }); err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+ return buf.String()
+}
+
+func parsePrivateKey(block []byte) any {
+ if k, err := x509.ParsePKCS1PrivateKey(block); err == nil {
+ return k
+ }
+ if k, err := x509.ParsePKCS8PrivateKey(block); err == nil {
+ return k
+ }
+ if k, err := x509.ParseECPrivateKey(block); err == nil {
+ return k
+ }
+ panic("Error: unable to parse private key")
+}
+
+func pkcs12key(input string) string {
+ return pkcs12keyPass("", input)
+}
+
+func pkcs12certPass(pass, input string) string {
+ _, certificate, caCerts, err := gopkcs12.DecodeChain([]byte(input), pass)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ var pemData []byte
+ var buf bytes.Buffer
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: certificate.Raw,
+ }); err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ pemData = append(pemData, buf.Bytes()...)
+
+ for _, ca := range caCerts {
+ var buf bytes.Buffer
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: ca.Raw,
+ }); err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+ pemData = append(pemData, buf.Bytes()...)
+ }
+
+ // try to order certificate chain. If it fails we return
+ // the unordered raw pem data.
+ // This fails if multiple leaf or disjunct certs are provided.
+ ordered := fetchCertChains(pemData)
+
+ return string(ordered)
+}
+
+func pkcs12cert(input string) string {
+ return pkcs12certPass("", input)
+}
+
+func pemToPkcs12(cert, key string) string {
+ return pemToPkcs12Pass(cert, key, "")
+}
+
+func pemToPkcs12Pass(cert, key, pass string) string {
+ certPem, _ := pem.Decode([]byte(cert))
+
+ parsedCert, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ return certsToPkcs12(parsedCert, key, nil, pass)
+}
+
+func fullPemToPkcs12(cert, key string) string {
+ return fullPemToPkcs12Pass(cert, key, "")
+}
+
+func fullPemToPkcs12Pass(cert, key, pass string) string {
+ certPem, rest := pem.Decode([]byte(cert))
+
+ parsedCert, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ caCerts := make([]*x509.Certificate, 0)
+ for len(rest) > 0 {
+ caPem, restBytes := pem.Decode(rest)
+ rest = restBytes
+
+ caCert, err := x509.ParseCertificate(caPem.Bytes)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ caCerts = append(caCerts, caCert)
+ }
+
+ return certsToPkcs12(parsedCert, key, caCerts, pass)
+}
+
+func certsToPkcs12(cert *x509.Certificate, key string, caCerts []*x509.Certificate, password string) string {
+ keyPem, _ := pem.Decode([]byte(key))
+ parsedKey := parsePrivateKey(keyPem.Bytes)
+
+ pfx, err := gopkcs12.Modern.Encode(parsedKey, cert, caCerts, password)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+
+ return base64.StdEncoding.EncodeToString(pfx)
+}
diff --git a/k8-operator/packages/template/template.go b/k8-operator/packages/template/template.go
new file mode 100644
index 0000000000..d56b3da5d4
--- /dev/null
+++ b/k8-operator/packages/template/template.go
@@ -0,0 +1,67 @@
+package template
+
+import (
+ tpl "text/template"
+
+ "github.com/Masterminds/sprig/v3"
+)
+
+var customInfisicalSecretTemplateFunctions = tpl.FuncMap{
+ "pkcs12key": pkcs12key,
+ "pkcs12keyPass": pkcs12keyPass,
+ "pkcs12cert": pkcs12cert,
+ "pkcs12certPass": pkcs12certPass,
+
+ "pemToPkcs12": pemToPkcs12,
+ "pemToPkcs12Pass": pemToPkcs12Pass,
+ "fullPemToPkcs12": fullPemToPkcs12,
+ "fullPemToPkcs12Pass": fullPemToPkcs12Pass,
+
+ "filterPEM": filterPEM,
+ "filterCertChain": filterCertChain,
+
+ "jwkPublicKeyPem": jwkPublicKeyPem,
+ "jwkPrivateKeyPem": jwkPrivateKeyPem,
+
+ "toYaml": toYAML,
+ "fromYaml": fromYAML,
+
+ "decodeBase64ToBytes": decodeBase64ToBytes,
+ "encodeBase64": encodeBase64,
+}
+
+const (
+ errParse = "unable to parse template at key %s: %s"
+ errExecute = "unable to execute template at key %s: %s"
+ errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s"
+ errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s"
+ errParsePrivKey = "unable to parse private key type"
+ errUnmarshalJSON = "unable to unmarshal json: %s"
+ errMarshalJSON = "unable to marshal json: %s"
+
+ pemTypeCertificate = "CERTIFICATE"
+ pemTypeKey = "PRIVATE KEY"
+)
+
+func InitializeTemplateFunctions() {
+ templates := customInfisicalSecretTemplateFunctions
+
+ sprigFuncs := sprig.TxtFuncMap()
+ // removed for security reasons
+ delete(sprigFuncs, "env")
+ delete(sprigFuncs, "expandenv")
+
+ for k, v := range sprigFuncs {
+ // make sure we aren't overwriting any of our own functions
+ _, exists := templates[k]
+ if !exists {
+ templates[k] = v
+ }
+ }
+
+ customInfisicalSecretTemplateFunctions = templates
+}
+
+func GetTemplateFunctions() tpl.FuncMap {
+ return customInfisicalSecretTemplateFunctions
+}
diff --git a/k8-operator/packages/template/yaml.go b/k8-operator/packages/template/yaml.go
new file mode 100644
index 0000000000..5352d5a023
--- /dev/null
+++ b/k8-operator/packages/template/yaml.go
@@ -0,0 +1,30 @@
+package template
+
+import (
+ "fmt"
+ "strings"
+
+ "gopkg.in/yaml.v3"
+)
+
+func toYAML(v any) string {
+ data, err := yaml.Marshal(v)
+ if err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+
+ }
+ return strings.TrimSuffix(string(data), "\n")
+}
+
+// fromYAML converts a YAML document into a map[string]any.
+//
+// This is not a general-purpose YAML parser, and will not parse all valid
+// YAML documents.
+func fromYAML(str string) map[string]any {
+ mapData := map[string]any{}
+
+ if err := yaml.Unmarshal([]byte(str), &mapData); err != nil {
+ panic(fmt.Sprintf("Error: %v", err))
+ }
+ return mapData
+}