mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 03:27:38 +00:00
Merge pull request #2987 from Infisical/daniel/k8s-multi-managed-secrets
feat(k8-operator/infisicalsecret-crd): multiple secret references
This commit is contained in:
@@ -26,8 +26,8 @@ spec:
|
|||||||
name: <service-account-name>
|
name: <service-account-name>
|
||||||
namespace: <service-account-namespace>
|
namespace: <service-account-namespace>
|
||||||
|
|
||||||
managedSecretReference:
|
managedKubeSecretReferences:
|
||||||
secretName: managed-secret
|
- secretName: managed-secret
|
||||||
secretNamespace: default
|
secretNamespace: default
|
||||||
creationPolicy: "Orphan"
|
creationPolicy: "Orphan"
|
||||||
template:
|
template:
|
||||||
@@ -541,18 +541,32 @@ The managed secret properties specify where to store the secrets retrieved from
|
|||||||
This includes defining the name and namespace of the Kubernetes secret that will hold these secrets.
|
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.
|
The Infisical operator will automatically create the Kubernetes secret in the specified name/namespace and ensure it stays up-to-date.
|
||||||
|
|
||||||
<Accordion title="managedSecretReference">
|
<Note>
|
||||||
|
|
||||||
|
The `managedSecretReference` field is deprecated and will be removed in a future release.
|
||||||
|
Replace it with `managedKubeSecretReferences`, which now accepts an array of references to support multiple managed secrets in a single InfisicalSecret CRD.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```yaml
|
||||||
|
managedKubeSecretReferences:
|
||||||
|
- secretName: managed-secret
|
||||||
|
secretNamespace: default
|
||||||
|
creationPolicy: "Orphan"
|
||||||
|
```
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
<Accordion title="managedKubeSecretReferences">
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.secretName">
|
<Accordion title="managedKubeSecretReferences[].secretName">
|
||||||
The name of the managed Kubernetes secret to be created
|
The name of the managed Kubernetes secret to be created
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.secretNamespace">
|
<Accordion title="managedKubeSecretReferences[].secretNamespace">
|
||||||
The namespace of the managed Kubernetes secret to be created.
|
The namespace of the managed Kubernetes secret to be created.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.secretType">
|
<Accordion title="managedKubeSecretReferences[].secretType">
|
||||||
Override the default Opaque type for managed secrets with this field. Useful for creating kubernetes.io/dockerconfigjson secrets.
|
Override the default Opaque type for managed secrets with this field. Useful for creating kubernetes.io/dockerconfigjson secrets.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.creationPolicy">
|
<Accordion title="managedKubeSecretReferences[].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.
|
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.
|
||||||
This is useful for tools such as ArgoCD, where every resource requires an owner reference; otherwise, it will be pruned automatically.
|
This is useful for tools such as ArgoCD, where every resource requires an owner reference; otherwise, it will be pruned automatically.
|
||||||
|
|
||||||
@@ -573,18 +587,18 @@ This is useful for tools such as ArgoCD, where every resource requires an owner
|
|||||||
Fetching secrets from Infisical as is via the operator may not be enough. This is where templating functionality may be helpful.
|
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.
|
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 title="managedKubeSecretReferences[].template">
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.template.includeAllSecrets">
|
<Accordion title="managedKubeSecretReferences[].template.includeAllSecrets">
|
||||||
This property controls what secrets are included in your managed secret when using templates.
|
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.
|
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.**
|
**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.
|
When set to `false`, only secrets defined in the `managedKubeSecretReferences[].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.
|
Use this option when you would like to sync **only** a subset of secrets from Infisical to Kubernetes.
|
||||||
|
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="managedSecretReference.template.data">
|
<Accordion title="managedKubeSecretReferences[].template.data">
|
||||||
Define secret keys and their corresponding templates.
|
Define secret keys and their corresponding templates.
|
||||||
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
|
Each data value uses a Golang template with access to all secrets retrieved from the specified scope.
|
||||||
|
|
||||||
@@ -600,8 +614,8 @@ type TemplateSecret struct {
|
|||||||
#### Example template configuration:
|
#### Example template configuration:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
managedSecretReference:
|
managedKubeSecretReferences:
|
||||||
secretName: managed-secret
|
- secretName: managed-secret
|
||||||
secretNamespace: default
|
secretNamespace: default
|
||||||
template:
|
template:
|
||||||
includeAllSecrets: true
|
includeAllSecrets: true
|
||||||
@@ -652,10 +666,10 @@ The example below assumes that the `BINARY_KEY_BASE64` secret is stored as a bas
|
|||||||
The resulting managed secret will contain the decoded value of `BINARY_KEY_BASE64`.
|
The resulting managed secret will contain the decoded value of `BINARY_KEY_BASE64`.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
managedSecretReference:
|
managedKubeSecretReferences:
|
||||||
secretName: managed-secret
|
secretName: managed-secret
|
||||||
secretNamespace: default
|
secretNamespace: default
|
||||||
template:
|
template:
|
||||||
includeAllSecrets: true
|
includeAllSecrets: true
|
||||||
data:
|
data:
|
||||||
BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
|
BINARY_KEY: "{{ decodeBase64ToBytes .BINARY_KEY_BASE64.Value }}"
|
||||||
@@ -913,7 +927,7 @@ spec:
|
|||||||
..
|
..
|
||||||
authentication:
|
authentication:
|
||||||
...
|
...
|
||||||
managedSecretReference:
|
managedKubeSecretReferences:
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# 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.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: v0.8.5
|
version: v0.8.6
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# 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
|
# 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.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "v0.8.5"
|
appVersion: "v0.8.6"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
{{- if .Values.installCRDs }}
|
|
||||||
apiVersion: apiextensions.k8s.io/v1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
@@ -262,6 +261,48 @@ spec:
|
|||||||
hostAPI:
|
hostAPI:
|
||||||
description: Infisical host to pull secrets from
|
description: Infisical host to pull secrets from
|
||||||
type: string
|
type: string
|
||||||
|
managedKubeSecretReferences:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
creationPolicy:
|
||||||
|
default: Orphan
|
||||||
|
description: 'The Kubernetes Secret creation policy. Enum with
|
||||||
|
values: ''Owner'', ''Orphan''. Owner creates the secret and
|
||||||
|
sets .metadata.ownerReferences of the InfisicalSecret CRD that
|
||||||
|
created it. Orphan will not set the secret owner. This will
|
||||||
|
result in the secret being orphaned and not deleted when the
|
||||||
|
resource is deleted.'
|
||||||
|
type: string
|
||||||
|
secretName:
|
||||||
|
description: The name of the Kubernetes Secret
|
||||||
|
type: string
|
||||||
|
secretNamespace:
|
||||||
|
description: The name space where the Kubernetes Secret is located
|
||||||
|
type: string
|
||||||
|
secretType:
|
||||||
|
default: Opaque
|
||||||
|
description: 'The Kubernetes Secret type (experimental feature).
|
||||||
|
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||||
|
type: string
|
||||||
|
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:
|
||||||
|
- secretName
|
||||||
|
- secretNamespace
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
managedSecretReference:
|
managedSecretReference:
|
||||||
properties:
|
properties:
|
||||||
creationPolicy:
|
creationPolicy:
|
||||||
@@ -338,7 +379,6 @@ spec:
|
|||||||
- secretNamespace
|
- secretNamespace
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- managedSecretReference
|
|
||||||
- resyncInterval
|
- resyncInterval
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
@@ -426,4 +466,3 @@ status:
|
|||||||
plural: ""
|
plural: ""
|
||||||
conditions: []
|
conditions: []
|
||||||
storedVersions: []
|
storedVersions: []
|
||||||
{{- end }}
|
|
||||||
@@ -32,7 +32,7 @@ controllerManager:
|
|||||||
- ALL
|
- ALL
|
||||||
image:
|
image:
|
||||||
repository: infisical/kubernetes-operator
|
repository: infisical/kubernetes-operator
|
||||||
tag: v0.8.5
|
tag: v0.8.6
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpu: 500m
|
cpu: 500m
|
||||||
|
|||||||
@@ -134,9 +134,12 @@ type InfisicalSecretSpec struct {
|
|||||||
// +kubebuilder:validation:Optional
|
// +kubebuilder:validation:Optional
|
||||||
Authentication Authentication `json:"authentication"`
|
Authentication Authentication `json:"authentication"`
|
||||||
|
|
||||||
// +kubebuilder:validation:Required
|
// +kubebuilder:validation:Optional
|
||||||
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"`
|
ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"`
|
||||||
|
|
||||||
|
// +kubebuilder:validation:Optional
|
||||||
|
ManagedKubeSecretReferences []ManagedKubeSecretConfig `json:"managedKubeSecretReferences"`
|
||||||
|
|
||||||
// +kubebuilder:default:=60
|
// +kubebuilder:default:=60
|
||||||
ResyncInterval int `json:"resyncInterval"`
|
ResyncInterval int `json:"resyncInterval"`
|
||||||
|
|
||||||
|
|||||||
@@ -565,6 +565,13 @@ func (in *InfisicalSecretSpec) DeepCopyInto(out *InfisicalSecretSpec) {
|
|||||||
out.TokenSecretReference = in.TokenSecretReference
|
out.TokenSecretReference = in.TokenSecretReference
|
||||||
out.Authentication = in.Authentication
|
out.Authentication = in.Authentication
|
||||||
in.ManagedSecretReference.DeepCopyInto(&out.ManagedSecretReference)
|
in.ManagedSecretReference.DeepCopyInto(&out.ManagedSecretReference)
|
||||||
|
if in.ManagedKubeSecretReferences != nil {
|
||||||
|
in, out := &in.ManagedKubeSecretReferences, &out.ManagedKubeSecretReferences
|
||||||
|
*out = make([]ManagedKubeSecretConfig, len(*in))
|
||||||
|
for i := range *in {
|
||||||
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
out.TLS = in.TLS
|
out.TLS = in.TLS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -261,6 +261,48 @@ spec:
|
|||||||
hostAPI:
|
hostAPI:
|
||||||
description: Infisical host to pull secrets from
|
description: Infisical host to pull secrets from
|
||||||
type: string
|
type: string
|
||||||
|
managedKubeSecretReferences:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
creationPolicy:
|
||||||
|
default: Orphan
|
||||||
|
description: 'The Kubernetes Secret creation policy. Enum with
|
||||||
|
values: ''Owner'', ''Orphan''. Owner creates the secret and
|
||||||
|
sets .metadata.ownerReferences of the InfisicalSecret CRD
|
||||||
|
that created it. Orphan will not set the secret owner. This
|
||||||
|
will result in the secret being orphaned and not deleted when
|
||||||
|
the resource is deleted.'
|
||||||
|
type: string
|
||||||
|
secretName:
|
||||||
|
description: The name of the Kubernetes Secret
|
||||||
|
type: string
|
||||||
|
secretNamespace:
|
||||||
|
description: The name space where the Kubernetes Secret is located
|
||||||
|
type: string
|
||||||
|
secretType:
|
||||||
|
default: Opaque
|
||||||
|
description: 'The Kubernetes Secret type (experimental feature).
|
||||||
|
More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
|
||||||
|
type: string
|
||||||
|
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:
|
||||||
|
- secretName
|
||||||
|
- secretNamespace
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
managedSecretReference:
|
managedSecretReference:
|
||||||
properties:
|
properties:
|
||||||
creationPolicy:
|
creationPolicy:
|
||||||
@@ -339,7 +381,6 @@ spec:
|
|||||||
- secretNamespace
|
- secretNamespace
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- managedSecretReference
|
|
||||||
- resyncInterval
|
- resyncInterval
|
||||||
type: object
|
type: object
|
||||||
status:
|
status:
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ spec:
|
|||||||
secretsPath: "/path"
|
secretsPath: "/path"
|
||||||
recursive: true
|
recursive: true
|
||||||
|
|
||||||
managedSecretReference:
|
managedSecretReferences:
|
||||||
secretName: managed-secret
|
- secretName: managed-secret
|
||||||
secretNamespace: default
|
secretNamespace: default
|
||||||
creationPolicy: "Orphan" ## Owner | Orphan
|
creationPolicy: "Orphan" ## Owner | Orphan
|
||||||
# secretType: kubernetes.io/dockerconfigjson
|
# secretType: kubernetes.io/dockerconfigjson
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
package controllers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/constants"
|
|
||||||
"github.com/go-logr/logr"
|
|
||||||
v1 "k8s.io/api/apps/v1"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret"
|
|
||||||
const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying
|
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) ReconcileDeploymentsWithManagedSecrets(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret) (int, error) {
|
|
||||||
listOfDeployments := &v1.DeploymentList{}
|
|
||||||
err := r.Client.List(ctx, listOfDeployments, &client.ListOptions{Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace})
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", infisicalSecret.Spec.ManagedSecretReference.SecretNamespace, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
managedKubeSecretNameAndNamespace := types.NamespacedName{
|
|
||||||
Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
|
|
||||||
Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
|
|
||||||
}
|
|
||||||
|
|
||||||
managedKubeSecret := &corev1.Secret{}
|
|
||||||
err = r.Client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
// Iterate over the deployments and check if they use the managed secret
|
|
||||||
for _, deployment := range listOfDeployments.Items {
|
|
||||||
deployment := deployment
|
|
||||||
if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && r.IsDeploymentUsingManagedSecret(deployment, infisicalSecret) {
|
|
||||||
// Start a goroutine to reconcile the deployment
|
|
||||||
wg.Add(1)
|
|
||||||
go func(d v1.Deployment, s corev1.Secret) {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := r.ReconcileDeployment(ctx, logger, d, s); err != nil {
|
|
||||||
logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name))
|
|
||||||
}
|
|
||||||
}(deployment, *managedKubeSecret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the deployment uses managed secrets
|
|
||||||
func (r *InfisicalSecretReconciler) IsDeploymentUsingManagedSecret(deployment v1.Deployment, infisicalSecret v1alpha1.InfisicalSecret) bool {
|
|
||||||
managedSecretName := infisicalSecret.Spec.ManagedSecretReference.SecretName
|
|
||||||
for _, container := range deployment.Spec.Template.Spec.Containers {
|
|
||||||
for _, envFrom := range container.EnvFrom {
|
|
||||||
if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, env := range container.Env {
|
|
||||||
if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, volume := range deployment.Spec.Template.Spec.Volumes {
|
|
||||||
if volume.Secret != nil && volume.Secret.SecretName == managedSecretName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions.
|
|
||||||
// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment.
|
|
||||||
func (r *InfisicalSecretReconciler) ReconcileDeployment(ctx context.Context, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error {
|
|
||||||
annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name)
|
|
||||||
annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION]
|
|
||||||
|
|
||||||
if deployment.Annotations[annotationKey] == annotationValue &&
|
|
||||||
deployment.Spec.Template.Annotations[annotationKey] == annotationValue {
|
|
||||||
logger.Info(fmt.Sprintf("The [deploymentName=%v] is already using the most up to date managed secrets. No action required.", deployment.ObjectMeta.Name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info(fmt.Sprintf("Deployment is using outdated managed secret. Starting re-deployment [deploymentName=%v]", deployment.ObjectMeta.Name))
|
|
||||||
|
|
||||||
if deployment.Spec.Template.Annotations == nil {
|
|
||||||
deployment.Spec.Template.Annotations = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
deployment.Annotations[annotationKey] = annotationValue
|
|
||||||
deployment.Spec.Template.Annotations[annotationKey] = annotationValue
|
|
||||||
|
|
||||||
if err := r.Client.Update(ctx, &deployment); err != nil {
|
|
||||||
return fmt.Errorf("failed to update deployment annotation: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||||
|
|
||||||
|
defaultErrors "errors"
|
||||||
|
|
||||||
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1"
|
||||||
"github.com/Infisical/infisical/k8-operator/packages/api"
|
"github.com/Infisical/infisical/k8-operator/packages/api"
|
||||||
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers"
|
||||||
@@ -35,8 +37,6 @@ func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger {
|
|||||||
return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName)
|
return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)
|
|
||||||
|
|
||||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete
|
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete
|
||||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch
|
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch
|
||||||
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update
|
//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update
|
||||||
@@ -71,6 +71,30 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It's important we don't directly modify the CRD object, so we create a copy of it and move existing data into it.
|
||||||
|
managedKubeSecretReferences := infisicalSecretCRD.Spec.ManagedKubeSecretReferences
|
||||||
|
|
||||||
|
if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" && managedKubeSecretReferences != nil && len(managedKubeSecretReferences) > 0 {
|
||||||
|
errMessage := "InfisicalSecret CRD cannot have both managedSecretReference and managedKubeSecretReferences"
|
||||||
|
logger.Error(defaultErrors.New(errMessage), errMessage)
|
||||||
|
return ctrl.Result{}, defaultErrors.New(errMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" {
|
||||||
|
logger.Info("\n\n\nThe field `managedSecretReference` will be deprecated in the near future, please use `managedKubeSecretReferences` instead.\n\nRefer to the documentation for more information: https://infisical.com/docs/integrations/platforms/kubernetes/infisical-secret-crd\n\n\n")
|
||||||
|
|
||||||
|
if managedKubeSecretReferences == nil {
|
||||||
|
managedKubeSecretReferences = []secretsv1alpha1.ManagedKubeSecretConfig{}
|
||||||
|
}
|
||||||
|
managedKubeSecretReferences = append(managedKubeSecretReferences, infisicalSecretCRD.Spec.ManagedSecretReference)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(managedKubeSecretReferences) == 0 {
|
||||||
|
errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` field"
|
||||||
|
logger.Error(defaultErrors.New(errMessage), errMessage)
|
||||||
|
return ctrl.Result{}, defaultErrors.New(errMessage)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove finalizers if they exist. This is to support previous InfisicalSecret CRD's that have finalizers on them.
|
// Remove finalizers if they exist. This is to support previous InfisicalSecret CRD's that have finalizers on them.
|
||||||
// In order to delete secrets with finalizers, we first remove the finalizers so we can use the simplified and improved deletion process
|
// In order to delete secrets with finalizers, we first remove the finalizers so we can use the simplified and improved deletion process
|
||||||
if !infisicalSecretCRD.ObjectMeta.DeletionTimestamp.IsZero() && len(infisicalSecretCRD.ObjectMeta.Finalizers) > 0 {
|
if !infisicalSecretCRD.ObjectMeta.DeletionTimestamp.IsZero() && len(infisicalSecretCRD.ObjectMeta.Finalizers) > 0 {
|
||||||
@@ -127,7 +151,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
api.API_CA_CERTIFICATE = ""
|
api.API_CA_CERTIFICATE = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.ReconcileInfisicalSecret(ctx, logger, infisicalSecretCRD)
|
err = r.ReconcileInfisicalSecret(ctx, logger, infisicalSecretCRD, managedKubeSecretReferences)
|
||||||
r.SetReadyToSyncSecretsConditions(ctx, &infisicalSecretCRD, err)
|
r.SetReadyToSyncSecretsConditions(ctx, &infisicalSecretCRD, err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -138,7 +162,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalSecretCRD.Spec.ManagedSecretReference)
|
numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences)
|
||||||
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime))
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ var infisicalSecretTemplateFunctions = template.FuncMap{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReference v1alpha1.ManagedKubeSecretConfig, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
||||||
plainProcessedSecrets := make(map[string][]byte)
|
plainProcessedSecrets := make(map[string][]byte)
|
||||||
secretType := infisicalSecret.Spec.ManagedSecretReference.SecretType
|
secretType := managedSecretReference.SecretType
|
||||||
managedTemplateData := infisicalSecret.Spec.ManagedSecretReference.Template
|
managedTemplateData := managedSecretReference.Template
|
||||||
|
|
||||||
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
||||||
for _, secret := range secretsFromAPI {
|
for _, secret := range secretsFromAPI {
|
||||||
@@ -226,8 +226,8 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
|
|||||||
// create a new secret as specified by the managed secret spec of CRD
|
// create a new secret as specified by the managed secret spec of CRD
|
||||||
newKubeSecretInstance := &corev1.Secret{
|
newKubeSecretInstance := &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
|
Name: managedSecretReference.SecretName,
|
||||||
Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
|
Namespace: managedSecretReference.SecretNamespace,
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
},
|
},
|
||||||
@@ -235,7 +235,7 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
|
|||||||
Data: plainProcessedSecrets,
|
Data: plainProcessedSecrets,
|
||||||
}
|
}
|
||||||
|
|
||||||
if infisicalSecret.Spec.ManagedSecretReference.CreationPolicy == "Owner" {
|
if managedSecretReference.CreationPolicy == "Owner" {
|
||||||
// Set InfisicalSecret instance as the owner and controller of the managed secret
|
// Set InfisicalSecret instance as the owner and controller of the managed secret
|
||||||
err := ctrl.SetControllerReference(&infisicalSecret, newKubeSecretInstance, r.Scheme)
|
err := ctrl.SetControllerReference(&infisicalSecret, newKubeSecretInstance, r.Scheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -252,8 +252,8 @@ func (r *InfisicalSecretReconciler) createInfisicalManagedKubeSecret(ctx context
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, managedSecretReference v1alpha1.ManagedKubeSecretConfig, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error {
|
||||||
managedTemplateData := infisicalSecret.Spec.ManagedSecretReference.Template
|
managedTemplateData := managedSecretReference.Template
|
||||||
|
|
||||||
plainProcessedSecrets := make(map[string][]byte)
|
plainProcessedSecrets := make(map[string][]byte)
|
||||||
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets {
|
||||||
@@ -337,7 +337,7 @@ func (r *InfisicalSecretReconciler) updateResourceVariables(infisicalSecret v1al
|
|||||||
infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] = resourceVariables
|
infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] = resourceVariables
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret) error {
|
func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig) error {
|
||||||
|
|
||||||
resourceVariables := r.getResourceVariables(infisicalSecret)
|
resourceVariables := r.getResourceVariables(infisicalSecret)
|
||||||
infisicalClient := resourceVariables.InfisicalClient
|
infisicalClient := resourceVariables.InfisicalClient
|
||||||
@@ -361,10 +361,11 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, managedSecretReference := range managedKubeSecretReferences {
|
||||||
// Look for managed secret by name and namespace
|
// Look for managed secret by name and namespace
|
||||||
managedKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
managedKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{
|
||||||
Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
|
Name: managedSecretReference.SecretName,
|
||||||
Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
|
Namespace: managedSecretReference.SecretNamespace,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil && !k8Errors.IsNotFound(err) {
|
if err != nil && !k8Errors.IsNotFound(err) {
|
||||||
@@ -420,13 +421,24 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
|
|||||||
logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy))
|
logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return errors.New("no authentication method provided yet. Please configure a authentication method then try again")
|
return errors.New("no authentication method provided. Please configure a authentication method then try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !updateDetails.Modified {
|
||||||
|
logger.Info("ReconcileInfisicalSecret: No secrets modified so reconcile not needed")
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if managedKubeSecret == nil {
|
if managedKubeSecret == nil {
|
||||||
return r.createInfisicalManagedKubeSecret(ctx, logger, infisicalSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
if err := r.createInfisicalManagedKubeSecret(ctx, logger, infisicalSecret, managedSecretReference, plainTextSecretsFromApi, updateDetails.ETag); err != nil {
|
||||||
|
return fmt.Errorf("failed to create managed secret [err=%s]", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return r.updateInfisicalManagedKubeSecret(ctx, logger, infisicalSecret, *managedKubeSecret, plainTextSecretsFromApi, updateDetails.ETag)
|
if err := r.updateInfisicalManagedKubeSecret(ctx, logger, managedSecretReference, *managedKubeSecret, plainTextSecretsFromApi, updateDetails.ETag); err != nil {
|
||||||
|
return fmt.Errorf("failed to update managed secret [err=%s]", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,17 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig) (int, error) {
|
||||||
|
for _, managedSecret := range managedSecrets {
|
||||||
|
_, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName))
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the deployment uses managed secrets
|
// Check if the deployment uses managed secrets
|
||||||
func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool {
|
||||||
managedSecretName := managedSecret.SecretName
|
managedSecretName := managedSecret.SecretName
|
||||||
|
|||||||
Reference in New Issue
Block a user