161 lines
5.5 KiB
Go
161 lines
5.5 KiB
Go
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2025 lerentis, https://git.uploadfilter24.eu/lerentis
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
package controller
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"os"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
|
|
"github.com/bitwarden/sdk-go"
|
|
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
|
|
type BitwardenSecretReconciler struct {
|
|
client.Client
|
|
Scheme *runtime.Scheme
|
|
}
|
|
|
|
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets,verbs=get;list;watch;create;update;patch;delete
|
|
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets/status,verbs=get;update;patch
|
|
// +kubebuilder:rbac:groups=lerentis.uploadfilter24.eu.lerentis.uploadfilter24.eu,resources=bitwardensecrets/finalizers,verbs=update
|
|
|
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
|
// move the current state of the cluster closer to the desired state.
|
|
// TODO(user): Modify the Reconcile function to compare the state specified by
|
|
// the BitwardenSecret object against the actual cluster state, and then
|
|
// perform operations to make the cluster state reflect the state specified by
|
|
// the user.
|
|
//
|
|
// For more details, check Reconcile and its Result here:
|
|
// - 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) {
|
|
olog := log.FromContext(ctx)
|
|
|
|
// 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
|
|
}
|
|
|
|
// SetupWithManager sets up the controller with the Manager.
|
|
func (r *BitwardenSecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|
return ctrl.NewControllerManagedBy(mgr).
|
|
For(&lerentisuploadfilter24euv1.BitwardenSecret{}).
|
|
Named("bitwardensecret").
|
|
Complete(r)
|
|
}
|