mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-03 23:26:09 +00:00
feat: added template support in operator
This commit is contained in:
@@ -147,6 +147,20 @@ type MangedKubeSecretConfig struct {
|
|||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
// +kubebuilder:default:=Orphan
|
// +kubebuilder:default:=Orphan
|
||||||
CreationPolicy string `json:"creationPolicy"`
|
CreationPolicy string `json:"creationPolicy"`
|
||||||
|
|
||||||
|
// The template to transform the secret data
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
Template *InfisicalSecretTemplate `json:"template,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CaReference struct {
|
type CaReference struct {
|
||||||
|
|||||||
@@ -283,6 +283,20 @@ spec:
|
|||||||
description: 'The Kubernetes Secret type (experimental feature).
|
description: 'The Kubernetes Secret type (experimental feature).
|
||||||
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||||
type: string
|
type: string
|
||||||
|
template:
|
||||||
|
description: The template to transform the secret data
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
description: The template key values
|
||||||
|
type: object
|
||||||
|
includeAllSecrets:
|
||||||
|
description: This injects all retrieved secrets into the top
|
||||||
|
level of your template. Secrets defined in the template
|
||||||
|
will take precedence over the injected ones.
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- secretName
|
- secretName
|
||||||
- secretNamespace
|
- secretNamespace
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
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:
|
||||||
|
hostAPI: https://app.infisical.com/api
|
||||||
|
resyncInterval: 10
|
||||||
|
# tls:
|
||||||
|
# caRef:
|
||||||
|
# secretName: custom-ca-certificate
|
||||||
|
# secretNamespace: default
|
||||||
|
# key: ca.crt
|
||||||
|
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 # Wether 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>
|
||||||
|
serviceAccountTokenPath: "/path/to/your/service-account/token" # Optional, defaults to /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||||
|
|
||||||
|
# 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=your_client_id # This field is optional, and will default to "https://management.azure.com/" if nothing is provided.
|
||||||
|
|
||||||
|
# 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>
|
||||||
|
serviceAccountKeyFilePath: "/path/to-service-account-key-file-path.json"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
template:
|
||||||
|
includeAllSecrets: true
|
||||||
|
data:
|
||||||
|
SSH_KEY: "{{ .KEY.SecretPath }} {{ .KEY.Value }}"
|
||||||
|
creationPolicy: "Orphan" ## Owner | Orphan
|
||||||
|
# secretType: kubernetes.io/dockerconfigjson
|
||||||
|
|
||||||
|
# # To be depreciated soon
|
||||||
|
# tokenSecretReference:
|
||||||
|
# secretName: service-token
|
||||||
|
# secretNamespace: default
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
@@ -225,12 +227,44 @@ func (r *InfisicalSecretReconciler) GetInfisicalServiceAccountCredentialsFromKub
|
|||||||
return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil
|
return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TemplateSecret struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
SecretPath string `json:"secretPath"`
|
||||||
|
}
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
||||||
plainProcessedSecrets := make(map[string][]byte)
|
plainProcessedSecrets := make(map[string][]byte)
|
||||||
secretType := infisicalSecret.Spec.ManagedSecretReference.SecretType
|
secretType := infisicalSecret.Spec.ManagedSecretReference.SecretType
|
||||||
|
managedTemplateData := infisicalSecret.Spec.ManagedSecretReference.Template
|
||||||
|
|
||||||
for _, secret := range secretsFromAPI {
|
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
||||||
plainProcessedSecrets[secret.Key] = []byte(secret.Value) // plain process
|
for _, secret := range secretsFromAPI {
|
||||||
|
plainProcessedSecrets[secret.Key] = []byte(secret.Value) // plain process
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if managedTemplateData != nil {
|
||||||
|
secretKeyValue := make(map[string]TemplateSecret)
|
||||||
|
for _, secret := range secretsFromAPI {
|
||||||
|
secretKeyValue[secret.Key] = TemplateSecret{
|
||||||
|
Value: secret.Value,
|
||||||
|
SecretPath: secret.SecretPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tmplKey, userTmpl := range managedTemplateData.Data {
|
||||||
|
tmpl, err := template.New("secret-templates").Parse(userTmpl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to compile template: %s", tmplKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
err = tmpl.Execute(buf, secretKeyValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to execute template: %s", tmplKey, err)
|
||||||
|
}
|
||||||
|
plainProcessedSecrets[tmplKey] = buf.Bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy labels and annotations from InfisicalSecret CRD
|
// copy labels and annotations from InfisicalSecret CRD
|
||||||
@@ -285,10 +319,38 @@ func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) UpdateInfisicalManagedKubeSecret(ctx context.Context, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
func (r *InfisicalSecretReconciler) UpdateInfisicalManagedKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
||||||
|
managedTemplateData := infisicalSecret.Spec.ManagedSecretReference.Template
|
||||||
|
|
||||||
plainProcessedSecrets := make(map[string][]byte)
|
plainProcessedSecrets := make(map[string][]byte)
|
||||||
for _, secret := range secretsFromAPI {
|
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
||||||
plainProcessedSecrets[secret.Key] = []byte(secret.Value)
|
for _, secret := range secretsFromAPI {
|
||||||
|
plainProcessedSecrets[secret.Key] = []byte(secret.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if managedTemplateData != nil {
|
||||||
|
secretKeyValue := make(map[string]TemplateSecret)
|
||||||
|
for _, secret := range secretsFromAPI {
|
||||||
|
secretKeyValue[secret.Key] = TemplateSecret{
|
||||||
|
Value: secret.Value,
|
||||||
|
SecretPath: secret.SecretPath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for tmplKey, userTmpl := range managedTemplateData.Data {
|
||||||
|
tmpl, err := template.New("secret-templates").Parse(userTmpl)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to compile template: %s", tmplKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
err = tmpl.Execute(buf, secretKeyValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to execute template: %s", tmplKey, err)
|
||||||
|
}
|
||||||
|
plainProcessedSecrets[tmplKey] = buf.Bytes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the Annotations map if it's nil
|
// Initialize the Annotations map if it's nil
|
||||||
@@ -434,7 +496,7 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
|
|||||||
if managedKubeSecret == nil {
|
if managedKubeSecret == nil {
|
||||||
return r.CreateInfisicalManagedKubeSecret(ctx, infisicalSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
return r.CreateInfisicalManagedKubeSecret(ctx, infisicalSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
||||||
} else {
|
} else {
|
||||||
return r.UpdateInfisicalManagedKubeSecret(ctx, *managedKubeSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
return r.UpdateInfisicalManagedKubeSecret(ctx, infisicalSecret, *managedKubeSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ func main() {
|
|||||||
var metricsAddr string
|
var metricsAddr string
|
||||||
var enableLeaderElection bool
|
var enableLeaderElection bool
|
||||||
var probeAddr string
|
var probeAddr string
|
||||||
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
|
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8082", "The address the metric endpoint binds to.")
|
||||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||||
"Enable leader election for controller manager. "+
|
"Enable leader election for controller manager. "+
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ type RequestUpdateUpdateDetails struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SingleEnvironmentVariable struct {
|
type SingleEnvironmentVariable struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
Type string `json:"type"`
|
SecretPath string `json:"secretPath"`
|
||||||
ID string `json:"_id"`
|
Type string `json:"type"`
|
||||||
|
ID string `json:"_id"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,10 +69,11 @@ func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalCl
|
|||||||
for _, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
|
|
||||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||||
Key: secret.SecretKey,
|
Key: secret.SecretKey,
|
||||||
Value: secret.SecretValue,
|
Value: secret.SecretValue,
|
||||||
Type: secret.Type,
|
Type: secret.Type,
|
||||||
ID: secret.ID,
|
ID: secret.ID,
|
||||||
|
SecretPath: secret.SecretPath,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +121,11 @@ func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClien
|
|||||||
for _, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
|
|
||||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||||
Key: secret.SecretKey,
|
Key: secret.SecretKey,
|
||||||
Value: secret.SecretValue,
|
Value: secret.SecretValue,
|
||||||
Type: secret.Type,
|
Type: secret.Type,
|
||||||
ID: secret.ID,
|
ID: secret.ID,
|
||||||
|
SecretPath: secret.SecretPath,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,10 +185,11 @@ func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalCli
|
|||||||
|
|
||||||
for _, secret := range secrets {
|
for _, secret := range secrets {
|
||||||
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{
|
||||||
Key: secret.SecretKey,
|
Key: secret.SecretKey,
|
||||||
Value: secret.SecretValue,
|
Value: secret.SecretValue,
|
||||||
Type: secret.Type,
|
Type: secret.Type,
|
||||||
ID: secret.ID,
|
ID: secret.ID,
|
||||||
|
SecretPath: secret.SecretPath,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user