mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #3007 from akhilmhdh/feat/base64-decode-in-operator
Base64 decode in operator
This commit is contained in:
@@ -5,6 +5,7 @@ description: "Learn how to use the InfisicalSecret CRD to fetch secrets from Inf
|
||||
---
|
||||
|
||||
Once you have installed the operator to your cluster, you'll need to create a `InfisicalSecret` custom resource definition (CRD).
|
||||
In this CRD, you'll define the authentication method to use, the secrets to fetch, and the target location to store the secrets within your cluster.
|
||||
|
||||
```yaml example-infisical-secret-crd.yaml
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
@@ -19,101 +20,28 @@ spec:
|
||||
hostAPI: https://app.infisical.com/api
|
||||
resyncInterval: 10
|
||||
authentication:
|
||||
# Make sure to only have 1 authentication method defined, serviceToken/universalAuth.
|
||||
# If you have multiple authentication methods defined, it may cause issues.
|
||||
|
||||
# (Deprecated) Service Token Auth
|
||||
serviceToken:
|
||||
serviceTokenSecretReference:
|
||||
secretName: service-token
|
||||
secretNamespace: default
|
||||
secretsScope:
|
||||
envSlug: <env-slug>
|
||||
secretsPath: <secrets-path>
|
||||
recursive: true
|
||||
|
||||
# Universal Auth
|
||||
universalAuth:
|
||||
secretsScope:
|
||||
projectSlug: new-ob-em
|
||||
envSlug: dev # "dev", "staging", "prod", etc..
|
||||
secretsPath: "/" # Root is "/"
|
||||
recursive: true # Whether or not to use recursive mode (Fetches all secrets in an environment from a given secret path, and all folders inside the path) / defaults to false
|
||||
credentialsRef:
|
||||
secretName: universal-auth-credentials
|
||||
secretNamespace: default
|
||||
|
||||
# Native Kubernetes Auth
|
||||
kubernetesAuth:
|
||||
identityId: <machine-identity-id>
|
||||
serviceAccountRef:
|
||||
name: <service-account-name>
|
||||
namespace: <service-account-namespace>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# AWS IAM Auth
|
||||
awsIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# Azure Auth
|
||||
azureAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
resource: https://management.azure.com/&client_id=CLIENT_ID # (Optional) This is the Azure resource that you want to access. For example, "https://management.azure.com/". If no value is provided, it will default to "https://management.azure.com/"
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP ID Token Auth
|
||||
gcpIdTokenAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
# GCP IAM Auth
|
||||
gcpIamAuth:
|
||||
identityId: <your-machine-identity-id>
|
||||
|
||||
# secretsScope is identical to the secrets scope in the universalAuth field in this sample.
|
||||
secretsScope:
|
||||
projectSlug: your-project-slug
|
||||
envSlug: prod
|
||||
secretsPath: "/path"
|
||||
recursive: true
|
||||
|
||||
managedSecretReference:
|
||||
secretName: managed-secret
|
||||
secretNamespace: default
|
||||
creationPolicy: "Orphan" ## Owner | Orphan
|
||||
# template:
|
||||
# includeAllSecrets: true
|
||||
# data:
|
||||
# CUSTOM_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||
# secretType: kubernetes.io/dockerconfigjson
|
||||
creationPolicy: "Orphan"
|
||||
template:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
NEW_KEY_NAME: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||
KEY_WITH_BINARY_VALUE: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||
```
|
||||
|
||||
### InfisicalSecret CRD properties
|
||||
## CRD properties
|
||||
|
||||
### Generic
|
||||
|
||||
The following properties help define what instance of Infisical the operator will interact with, the interval it will sync secrets and any CA certificates that may be required to connect.
|
||||
|
||||
<Accordion title="hostAPI">
|
||||
If you are fetching secrets from a self-hosted instance of Infisical set the value of `hostAPI` to
|
||||
@@ -165,10 +93,12 @@ When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
|
||||
CA certificate to use for connecting to the Infisical instance with SSL/TLS.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="authentication">
|
||||
This block defines the method that will be used to authenticate with Infisical
|
||||
so that secrets can be fetched
|
||||
</Accordion>
|
||||
### Authentication methods
|
||||
|
||||
To retrieve the requested secrets, the operator must first authenticate with Infisical.
|
||||
The list of available authentication methods are shown below.
|
||||
|
||||
<Accordion title="authentication"></Accordion>
|
||||
|
||||
<Accordion title="authentication.universalAuth">
|
||||
The universal machine identity authentication method is used to authenticate with Infisical. The client ID and client secret needs to be stored in a Kubernetes secret. This block defines the reference to the name and namespace of secret that stores these credentials.
|
||||
@@ -605,13 +535,13 @@ spec:
|
||||
|
||||
</Accordion>
|
||||
|
||||
### Operator managed secrets
|
||||
|
||||
The managed secret properties specify where to store the secrets retrieved from your Infisical project.
|
||||
This includes defining the name and namespace of the Kubernetes secret that will hold these secrets.
|
||||
The Infisical operator will automatically create the Kubernetes secret in the specified name/namespace and ensure it stays up-to-date.
|
||||
|
||||
<Accordion title="managedSecretReference">
|
||||
The `managedSecretReference` field is used to define the target location for storing secrets retrieved from an Infisical project.
|
||||
This field requires specifying both the name and namespace of the Kubernetes secret that will hold these secrets.
|
||||
The Infisical operator will automatically create the Kubernetes secret with the specified name/namespace and keep it continuously updated.
|
||||
|
||||
Note: The managed secret be should be created in the same namespace as the deployment that will use it.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.secretName">
|
||||
The name of the managed Kubernetes secret to be created
|
||||
@@ -621,57 +551,6 @@ The namespace of the managed Kubernetes secret to be created.
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.secretType">
|
||||
Override the default Opaque type for managed secrets with this field. Useful for creating kubernetes.io/dockerconfigjson secrets.
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.template">
|
||||
Templates enable you to transform data from Infisical before storing it as a Kubernetes Secret.
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.template.includeAllSecrets">
|
||||
When set to true, this option injects all secrets retrieved from Infisical into your configuration.
|
||||
Secrets defined in the template will override the automatically injected secrets.
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.template.data">
|
||||
Define secret keys and their corresponding templates.
|
||||
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
|
||||
|
||||
Secrets are structured as follows:
|
||||
|
||||
```golang
|
||||
type TemplateSecret struct {
|
||||
Value string `json:"value"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Example template configuration:
|
||||
|
||||
```golang
|
||||
managedSecretReference:
|
||||
secretName: managed-secret
|
||||
secretNamespace: default
|
||||
template:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
NEW_KEY: "{{ .KEY1.SecretPath }} {{ .KEY1.Value }}"
|
||||
```
|
||||
|
||||
When you run the following command:
|
||||
|
||||
```bash
|
||||
kubectl get secret managed-secret -o jsonpath='{.data}'
|
||||
```
|
||||
|
||||
You'll receive Kubernetes secrets output that includes the NEW_KEY:
|
||||
|
||||
```bash
|
||||
{... "KEY":"d29ybGQ=","NEW_KEY":"LyBoZWxsbw=="}
|
||||
```
|
||||
|
||||
When you set `includeAllSecrets` as `false` the Kubernetes secrets outputs will be:
|
||||
|
||||
```bash
|
||||
{"NEW_KEY":"LyBoZWxsbw=="}
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.creationPolicy">
|
||||
Creation polices allow you to control whether or not owner references should be added to the managed Kubernetes secret that is generated by the Infisical operator.
|
||||
@@ -689,7 +568,104 @@ This is useful for tools such as ArgoCD, where every resource requires an owner
|
||||
|
||||
</Accordion>
|
||||
|
||||
### Apply the InfisicalSecret CRD to your cluster
|
||||
### Manged secret templating
|
||||
|
||||
Fetching secrets from Infisical as is via the operator may not be enough. This is where templating functionality may be helpful.
|
||||
Using Go templates, you can format, combine, and create new key-value pairs from secrets fetched from Infisical before storing them as Kubernetes Secrets.
|
||||
|
||||
<Accordion title="managedSecretReference.template">
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.template.includeAllSecrets">
|
||||
This property controls what secrets are included in your managed secret when using templates.
|
||||
When set to `true`, all secrets fetched from your Infisical project will be added into your managed Kubernetes secret resource.
|
||||
**Use this option when you would like to sync all secrets from Infisical to Kubernetes but want to template a subset of them.**
|
||||
|
||||
When set to `false`, only secrets defined in the `managedSecretReference.template.data` field of the template will be included in the managed secret.
|
||||
Use this option when you would like to sync **only** a subset of secrets from Infisical to Kubernetes.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="managedSecretReference.template.data">
|
||||
Define secret keys and their corresponding templates.
|
||||
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
|
||||
|
||||
Secrets are structured as follows:
|
||||
|
||||
```golang
|
||||
type TemplateSecret struct {
|
||||
Value string `json:"value"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Example template configuration:
|
||||
|
||||
```yaml
|
||||
managedSecretReference:
|
||||
secretName: managed-secret
|
||||
secretNamespace: default
|
||||
template:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
# Create new secret key that doesn't exist in your Infisical project using values of other secrets
|
||||
NEW_KEY: "{{ .DB_PASSWORD.Value }}"
|
||||
# Override an existing secret key in Infisical project with a new value using values of other secrets
|
||||
API_URL: "https://api.{{.COMPANY_NAME.Value}}.{{.REGION.Value}}.com"
|
||||
```
|
||||
|
||||
For this example, let's assume the following secrets exist in your Infisical project:
|
||||
|
||||
```
|
||||
DB_PASSWORD = "secret123"
|
||||
COMPANY_NAME = "acme"
|
||||
REGION = "us-east-1"
|
||||
API_URL = "old-url" # This will be overridden
|
||||
```
|
||||
|
||||
The resulting managed Kubernetes secret will then contain:
|
||||
|
||||
```
|
||||
# Original secrets (from includeAllSecrets: true)
|
||||
DB_PASSWORD = "secret123"
|
||||
COMPANY_NAME = "acme"
|
||||
REGION = "us-east-1"
|
||||
|
||||
# New and overridden templated secrets
|
||||
NEW_KEY = "secret123" # New secret created from template
|
||||
API_URL = "https://api.acme.us-east-1.com" # Existing secret overridden by template
|
||||
```
|
||||
|
||||
To help transform your secrets further, the operator provides a set of built-in functions that you can use in your templates.
|
||||
|
||||
### Available templating functions
|
||||
|
||||
<Accordion title="decodeBase64ToBytes">
|
||||
**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
|
||||
managedSecretReference:
|
||||
secretName: managed-secret
|
||||
secretNamespace: default
|
||||
template:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
</Accordion>
|
||||
|
||||
## Applying CRD
|
||||
|
||||
Once you have configured the InfisicalSecret CRD with the required fields, you can apply it to your cluster.
|
||||
After applying, you should notice that the managed secret has been created in the desired namespace your specified.
|
||||
@@ -698,8 +674,6 @@ After applying, you should notice that the managed secret has been created in th
|
||||
kubectl apply -f example-infisical-secret-crd.yaml
|
||||
```
|
||||
|
||||
### Verify managed secret creation
|
||||
|
||||
To verify that the operator has successfully created the managed secret, you can check the secrets in the namespace that was specified.
|
||||
|
||||
```bash
|
||||
@@ -714,7 +688,7 @@ kubectl get secrets -n <namespace of managed secret>
|
||||
|
||||
## Using managed secret in your deployment
|
||||
|
||||
Incorporating the managed secret created by the operator into your deployment can be achieved through several methods.
|
||||
To make use of the managed secret created by the operator into your deployment can be achieved through several methods.
|
||||
Here, we will highlight three of the most common ways to utilize it. Learn more about Kubernetes secrets [here](https://kubernetes.io/docs/concepts/configuration/secret/)
|
||||
|
||||
<Accordion title="envFrom">
|
||||
@@ -960,4 +934,4 @@ metadata:
|
||||
type: Opaque
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
</Accordion>
|
||||
|
||||
@@ -13,9 +13,9 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: v0.8.2
|
||||
version: v0.8.3
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "v0.8.2"
|
||||
appVersion: "v0.8.3"
|
||||
|
||||
@@ -104,6 +104,7 @@ spec:
|
||||
includeAllSecrets: true
|
||||
data:
|
||||
SSH_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||
BINARY_KEY: "{{ toBase64DecodedString .BINARY_KEY_BASE64.Value }}"
|
||||
creationPolicy: "Orphan" ## Owner | Orphan
|
||||
# secretType: kubernetes.io/dockerconfigjson
|
||||
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -154,6 +155,16 @@ 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) []byte {
|
||||
decoded, err := base64.StdEncoding.DecodeString(encodedString)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
return decoded
|
||||
},
|
||||
}
|
||||
|
||||
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
||||
plainProcessedSecrets := make(map[string][]byte)
|
||||
secretType := infisicalSecret.Spec.ManagedSecretReference.SecretType
|
||||
@@ -175,7 +186,7 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
|
||||
}
|
||||
|
||||
for templateKey, userTemplate := range managedTemplateData.Data {
|
||||
tmpl, err := template.New("secret-templates").Parse(userTemplate)
|
||||
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||
}
|
||||
@@ -261,7 +272,7 @@ func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context
|
||||
}
|
||||
|
||||
for templateKey, userTemplate := range managedTemplateData.Data {
|
||||
tmpl, err := template.New("secret-templates").Parse(userTemplate)
|
||||
tmpl, err := template.New("secret-templates").Funcs(infisicalSecretTemplateFunctions).Parse(userTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err)
|
||||
}
|
||||
@@ -412,11 +423,6 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
|
||||
return errors.New("no authentication method provided yet. Please configure a authentication method then try again")
|
||||
}
|
||||
|
||||
if !updateDetails.Modified {
|
||||
logger.Info("ReconcileInfisicalSecret: No secrets modified so reconcile not needed")
|
||||
return nil
|
||||
}
|
||||
|
||||
if managedKubeSecret == nil {
|
||||
return r.createInfisicalManagedKubeSecret(ctx, logger, infisicalSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user