Compare commits
2 Commits
fbfe7b9986
...
feature/ku
Author | SHA1 | Date | |
---|---|---|---|
84f338426e
|
|||
ce3a6dfc6d
|
@ -28,11 +28,19 @@ import (
|
|||||||
|
|
||||||
// BitwardenSecretSpec defines the desired state of BitwardenSecret.
|
// BitwardenSecretSpec defines the desired state of BitwardenSecret.
|
||||||
type BitwardenSecretSpec struct {
|
type BitwardenSecretSpec struct {
|
||||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
Content []Element `json:"content"`
|
||||||
// Important: Run "make" to regenerate code after modifying this file
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
|
SecretType string `json:"secretType,omitempty"`
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Foo is an example field of BitwardenSecret. Edit bitwardensecret_types.go to remove/update
|
type Element struct {
|
||||||
Foo string `json:"foo,omitempty"`
|
SecretName string `json:"secretName"`
|
||||||
|
SecretRef string `json:"secretRef"`
|
||||||
|
SecretScope string `json:"secretScope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BitwardenSecretStatus defines the observed state of BitwardenSecret.
|
// BitwardenSecretStatus defines the observed state of BitwardenSecret.
|
||||||
|
@ -33,7 +33,7 @@ func (in *BitwardenSecret) DeepCopyInto(out *BitwardenSecret) {
|
|||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
out.Spec = in.Spec
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
out.Status = in.Status
|
out.Status = in.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +90,25 @@ func (in *BitwardenSecretList) DeepCopyObject() runtime.Object {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *BitwardenSecretSpec) DeepCopyInto(out *BitwardenSecretSpec) {
|
func (in *BitwardenSecretSpec) DeepCopyInto(out *BitwardenSecretSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
if in.Content != nil {
|
||||||
|
in, out := &in.Content, &out.Content
|
||||||
|
*out = make([]Element, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Labels != nil {
|
||||||
|
in, out := &in.Labels, &out.Labels
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Annotations != nil {
|
||||||
|
in, out := &in.Annotations, &out.Annotations
|
||||||
|
*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 BitwardenSecretSpec.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretSpec.
|
||||||
@ -206,6 +225,21 @@ func (in *BitwardenTemplateStatus) DeepCopy() *BitwardenTemplateStatus {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Element) DeepCopyInto(out *Element) {
|
||||||
|
*out = *in
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Element.
|
||||||
|
func (in *Element) DeepCopy() *Element {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Element)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *RegistryCredential) DeepCopyInto(out *RegistryCredential) {
|
func (in *RegistryCredential) DeepCopyInto(out *RegistryCredential) {
|
||||||
*out = *in
|
*out = *in
|
||||||
|
@ -39,11 +39,42 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
description: BitwardenSecretSpec defines the desired state of BitwardenSecret.
|
description: BitwardenSecretSpec defines the desired state of BitwardenSecret.
|
||||||
properties:
|
properties:
|
||||||
foo:
|
annotations:
|
||||||
description: Foo is an example field of BitwardenSecret. Edit bitwardensecret_types.go
|
additionalProperties:
|
||||||
to remove/update
|
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
content:
|
||||||
|
items:
|
||||||
|
properties:
|
||||||
|
secretName:
|
||||||
|
type: string
|
||||||
|
secretRef:
|
||||||
|
type: string
|
||||||
|
secretScope:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- secretName
|
||||||
|
- secretRef
|
||||||
|
- secretScope
|
||||||
|
type: object
|
||||||
|
type: array
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
labels:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
namespace:
|
||||||
|
type: string
|
||||||
|
secretType:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- content
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
type: object
|
||||||
status:
|
status:
|
||||||
description: BitwardenSecretStatus defines the observed state of BitwardenSecret.
|
description: BitwardenSecretStatus defines the observed state of BitwardenSecret.
|
||||||
type: object
|
type: object
|
||||||
|
@ -7,7 +7,6 @@ rules:
|
|||||||
- apiGroups:
|
- apiGroups:
|
||||||
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
||||||
resources:
|
resources:
|
||||||
- bitwardensecrets
|
|
||||||
- bitwardentemplates
|
- bitwardentemplates
|
||||||
- registrycredentials
|
- registrycredentials
|
||||||
verbs:
|
verbs:
|
||||||
@ -21,7 +20,6 @@ rules:
|
|||||||
- apiGroups:
|
- apiGroups:
|
||||||
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
||||||
resources:
|
resources:
|
||||||
- bitwardensecrets/finalizers
|
|
||||||
- bitwardentemplates/finalizers
|
- bitwardentemplates/finalizers
|
||||||
- registrycredentials/finalizers
|
- registrycredentials/finalizers
|
||||||
verbs:
|
verbs:
|
||||||
@ -29,7 +27,6 @@ rules:
|
|||||||
- apiGroups:
|
- apiGroups:
|
||||||
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
- lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu
|
||||||
resources:
|
resources:
|
||||||
- bitwardensecrets/status
|
|
||||||
- bitwardentemplates/status
|
- bitwardentemplates/status
|
||||||
- registrycredentials/status
|
- registrycredentials/status
|
||||||
verbs:
|
verbs:
|
||||||
|
1
go.mod
1
go.mod
@ -5,6 +5,7 @@ go 1.23.0
|
|||||||
godebug default=go1.23
|
godebug default=go1.23
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/bitwarden/sdk-go v1.0.2
|
||||||
github.com/onsi/ginkgo/v2 v2.22.0
|
github.com/onsi/ginkgo/v2 v2.22.0
|
||||||
github.com/onsi/gomega v1.36.1
|
github.com/onsi/gomega v1.36.1
|
||||||
k8s.io/apimachinery v0.32.1
|
k8s.io/apimachinery v0.32.1
|
||||||
|
2
go.sum
2
go.sum
@ -6,6 +6,8 @@ github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bitwarden/sdk-go v1.0.2 h1:krk5et4sfksLDDcrYHcs8f3jL/TGcQ1EShw4CG21JSI=
|
||||||
|
github.com/bitwarden/sdk-go v1.0.2/go.mod h1:RuYh+gqffp3h8wNUVWz1bvp2Pho10AFz+WIlI26iWY4=
|
||||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
|
@ -21,13 +21,19 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
|
"github.com/bitwarden/sdk-go"
|
||||||
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
|
lerentisuploadfilter24euv1 "github.com/lerentis/bitwarden-crd-operator/api/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BitwardenSecretReconciler reconciles a BitwardenSecret object
|
// BitwardenSecretReconciler reconciles a BitwardenSecret object
|
||||||
@ -50,9 +56,97 @@ type BitwardenSecretReconciler struct {
|
|||||||
// For more details, check Reconcile and its Result here:
|
// For more details, check Reconcile and its Result here:
|
||||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
|
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
|
||||||
func (r *BitwardenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
func (r *BitwardenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||||
_ = log.FromContext(ctx)
|
olog := log.FromContext(ctx)
|
||||||
|
|
||||||
// TODO(user): your logic here
|
// Fetch the BitwardenSecret instance
|
||||||
|
var bitwardenSecret lerentisuploadfilter24euv1.BitwardenSecret
|
||||||
|
if err := r.Get(ctx, req.NamespacedName, &bitwardenSecret); err != nil {
|
||||||
|
if errors.IsNotFound(err) {
|
||||||
|
olog.Info("BitwardenSecret resource not found. Ignoring since object must be deleted.")
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
olog.Error(err, "Failed to get BitwardenSecret.")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch secrets from Bitwarden
|
||||||
|
apiURL := os.Getenv("API_URL")
|
||||||
|
identityURL := os.Getenv("IDENTITY_URL")
|
||||||
|
|
||||||
|
client, _ := sdk.NewBitwardenClient(&apiURL, &identityURL)
|
||||||
|
|
||||||
|
accessToken := os.Getenv("ACCESS_TOKEN")
|
||||||
|
|
||||||
|
stateFile := os.Getenv("STATE_FILE")
|
||||||
|
|
||||||
|
err := client.AccessTokenLogin(accessToken, &stateFile)
|
||||||
|
if err != nil {
|
||||||
|
olog.Error(err, "Failed to authenticate with Bitwarden.")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secretData := make(map[string][]byte)
|
||||||
|
for _, element := range bitwardenSecret.Spec.Content {
|
||||||
|
resp, err := client.Secrets().Get(bitwardenSecret.Spec.ID)
|
||||||
|
if err != nil {
|
||||||
|
olog.Error(err, "Failed to fetch item from Bitwarden.")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item := resp.Value
|
||||||
|
|
||||||
|
var secretValue string
|
||||||
|
switch element.SecretScope {
|
||||||
|
case "login":
|
||||||
|
if element.SecretName == "username" {
|
||||||
|
secretValue = item.Login.Username
|
||||||
|
} else if element.SecretName == "password" {
|
||||||
|
secretValue = item.Login.Password
|
||||||
|
}
|
||||||
|
case "fields":
|
||||||
|
for _, field := range item.Fields {
|
||||||
|
if field.Name == element.SecretName {
|
||||||
|
secretValue = field.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "attachment":
|
||||||
|
for _, attachment := range item.Attachments {
|
||||||
|
if attachment.FileName == element.SecretName {
|
||||||
|
secretValue = attachment.File
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if secretValue != "" {
|
||||||
|
secretData[element.SecretRef] = []byte(base64.StdEncoding.EncodeToString([]byte(secretValue)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create or update Kubernetes secret
|
||||||
|
secret := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: bitwardenSecret.Spec.Name,
|
||||||
|
Namespace: bitwardenSecret.Spec.Namespace,
|
||||||
|
Labels: bitwardenSecret.Spec.Labels,
|
||||||
|
Annotations: bitwardenSecret.Spec.Annotations,
|
||||||
|
},
|
||||||
|
Data: secretData,
|
||||||
|
Type: corev1.SecretType(bitwardenSecret.Spec.SecretType),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.Client.Create(ctx, secret); err != nil && !errors.IsAlreadyExists(err) {
|
||||||
|
olog.Error(err, "Failed to create Secret.")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
if err := r.Client.Update(ctx, secret); err != nil {
|
||||||
|
olog.Error(err, "Failed to update Secret.")
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user