Use 1Password Client to initialize operator either with Connect or Service Accounts

This commit is contained in:
Volodymyr Zotov
2025-05-29 17:23:49 -05:00
parent 432f2c6cf6
commit 1498c223a5
8 changed files with 56 additions and 63 deletions

View File

@@ -35,8 +35,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/1Password/connect-sdk-go/connect"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them. // to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -54,6 +52,7 @@ import (
onepasswordcomv1 "github.com/1Password/onepassword-operator/api/v1" onepasswordcomv1 "github.com/1Password/onepassword-operator/api/v1"
"github.com/1Password/onepassword-operator/internal/controller" "github.com/1Password/onepassword-operator/internal/controller"
op "github.com/1Password/onepassword-operator/pkg/onepassword" op "github.com/1Password/onepassword-operator/pkg/onepassword"
opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
"github.com/1Password/onepassword-operator/version" "github.com/1Password/onepassword-operator/version"
//+kubebuilder:scaffold:imports //+kubebuilder:scaffold:imports
@@ -153,16 +152,16 @@ func main() {
} }
// Setup One Password Client // Setup One Password Client
opConnectClient, err := connect.NewClientFromEnvironment() opClient, err := opclient.NewClient(version.OperatorVersion)
if err != nil { if err != nil {
setupLog.Error(err, "unable to create Connect client") setupLog.Error(err, "unable to create 1Password client")
os.Exit(1) os.Exit(1)
} }
if err = (&controller.OnePasswordItemReconciler{ if err = (&controller.OnePasswordItemReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
OpConnectClient: opConnectClient, OpClient: opClient,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "OnePasswordItem") setupLog.Error(err, "unable to create controller", "controller", "OnePasswordItem")
os.Exit(1) os.Exit(1)
@@ -172,7 +171,7 @@ func main() {
if err = (&controller.DeploymentReconciler{ if err = (&controller.DeploymentReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Scheme: mgr.GetScheme(), Scheme: mgr.GetScheme(),
OpConnectClient: opConnectClient, OpClient: opClient,
OpAnnotationRegExp: r, OpAnnotationRegExp: r,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Deployment") setupLog.Error(err, "unable to create controller", "controller", "Deployment")
@@ -202,7 +201,7 @@ func main() {
} }
// Setup update secrets task // Setup update secrets task
updatedSecretsPoller := op.NewManager(mgr.GetClient(), opConnectClient, shouldAutoRestartDeployments()) updatedSecretsPoller := op.NewManager(mgr.GetClient(), opClient, shouldAutoRestartDeployments())
done := make(chan bool) done := make(chan bool)
ticker := time.NewTicker(getPollingIntervalForUpdatingSecrets()) ticker := time.NewTicker(getPollingIntervalForUpdatingSecrets())
go func() { go func() {

View File

@@ -31,11 +31,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/1Password/connect-sdk-go/connect"
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
"github.com/1Password/onepassword-operator/pkg/logs" "github.com/1Password/onepassword-operator/pkg/logs"
op "github.com/1Password/onepassword-operator/pkg/onepassword" op "github.com/1Password/onepassword-operator/pkg/onepassword"
opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
@@ -55,7 +54,7 @@ var logDeployment = logf.Log.WithName("controller_deployment")
type DeploymentReconciler struct { type DeploymentReconciler struct {
client.Client client.Client
Scheme *runtime.Scheme Scheme *runtime.Scheme
OpConnectClient connect.Client OpClient opclient.Client
OpAnnotationRegExp *regexp.Regexp OpAnnotationRegExp *regexp.Regexp
} }
@@ -196,7 +195,7 @@ func (r *DeploymentReconciler) handleApplyingDeployment(deployment *appsv1.Deplo
return nil return nil
} }
item, err := op.GetOnePasswordItemByPath(r.OpConnectClient, annotations[op.ItemPathAnnotation]) item, err := op.GetOnePasswordItemByPath(r.OpClient, annotations[op.ItemPathAnnotation])
if err != nil { if err != nil {
return fmt.Errorf("Failed to retrieve item: %v", err) return fmt.Errorf("Failed to retrieve item: %v", err)
} }

View File

@@ -28,12 +28,11 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/1Password/connect-sdk-go/connect"
onepasswordv1 "github.com/1Password/onepassword-operator/api/v1" onepasswordv1 "github.com/1Password/onepassword-operator/api/v1"
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
"github.com/1Password/onepassword-operator/pkg/logs" "github.com/1Password/onepassword-operator/pkg/logs"
op "github.com/1Password/onepassword-operator/pkg/onepassword" op "github.com/1Password/onepassword-operator/pkg/onepassword"
opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@@ -53,7 +52,7 @@ var finalizer = "onepassword.com/finalizer.secret"
type OnePasswordItemReconciler struct { type OnePasswordItemReconciler struct {
client.Client client.Client
Scheme *runtime.Scheme Scheme *runtime.Scheme
OpConnectClient connect.Client OpClient opclient.Client
} }
//+kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=onepassword.com,resources=onepassworditems,verbs=get;list;watch;create;update;patch;delete
@@ -164,7 +163,7 @@ func (r *OnePasswordItemReconciler) handleOnePasswordItem(resource *onepasswordv
secretType := resource.Type secretType := resource.Type
autoRestart := resource.Annotations[op.RestartDeploymentsAnnotation] autoRestart := resource.Annotations[op.RestartDeploymentsAnnotation]
item, err := op.GetOnePasswordItemByPath(r.OpConnectClient, resource.Spec.ItemPath) item, err := op.GetOnePasswordItemByPath(r.OpClient, resource.Spec.ItemPath)
if err != nil { if err != nil {
return fmt.Errorf("Failed to retrieve item: %v", err) return fmt.Errorf("Failed to retrieve item: %v", err)
} }

View File

@@ -3,6 +3,7 @@ package kubernetessecrets
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/1Password/onepassword-operator/pkg/onepassword/model"
"regexp" "regexp"
"strings" "strings"
@@ -11,8 +12,6 @@ import (
errs "errors" errs "errors"
"github.com/1Password/connect-sdk-go/onepassword"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
@@ -34,11 +33,11 @@ var ErrCannotUpdateSecretType = errs.New("Cannot change secret type. Secret type
var log = logf.Log var log = logf.Log
func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *onepassword.Item, autoRestart string, labels map[string]string, secretType string, ownerRef *metav1.OwnerReference) error { func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretName, namespace string, item *model.Item, autoRestart string, labels map[string]string, secretType string, ownerRef *metav1.OwnerReference) error {
itemVersion := fmt.Sprint(item.Version) itemVersion := fmt.Sprint(item.Version)
secretAnnotations := map[string]string{ secretAnnotations := map[string]string{
VersionAnnotation: itemVersion, VersionAnnotation: itemVersion,
ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID), ItemPathAnnotation: fmt.Sprintf("vaults/%v/items/%v", item.VaultID, item.ID),
} }
if autoRestart != "" { if autoRestart != "" {
@@ -92,7 +91,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
return nil return nil
} }
func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item onepassword.Item, ownerRef *metav1.OwnerReference) *corev1.Secret { func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotations map[string]string, labels map[string]string, secretType string, item model.Item, ownerRef *metav1.OwnerReference) *corev1.Secret {
var ownerRefs []metav1.OwnerReference var ownerRefs []metav1.OwnerReference
if ownerRef != nil { if ownerRef != nil {
ownerRefs = []metav1.OwnerReference{*ownerRef} ownerRefs = []metav1.OwnerReference{*ownerRef}
@@ -111,7 +110,7 @@ func BuildKubernetesSecretFromOnePasswordItem(name, namespace string, annotation
} }
} }
func BuildKubernetesSecretData(fields []*onepassword.ItemField, files []*onepassword.File) map[string][]byte { func BuildKubernetesSecretData(fields []model.ItemField, files []model.File) map[string][]byte {
secretData := map[string][]byte{} secretData := map[string][]byte{}
for i := 0; i < len(fields); i++ { for i := 0; i < len(fields); i++ {
if fields[i].Value != "" { if fields[i].Value != "" {

View File

@@ -2,6 +2,7 @@ package client
import ( import (
"errors" "errors"
"os"
"github.com/1Password/onepassword-operator/pkg/onepassword/client/connect" "github.com/1Password/onepassword-operator/pkg/onepassword/client/connect"
"github.com/1Password/onepassword-operator/pkg/onepassword/client/sdk" "github.com/1Password/onepassword-operator/pkg/onepassword/client/sdk"
@@ -16,29 +17,24 @@ type Client interface {
GetVaultsByTitle(title string) ([]model.Vault, error) GetVaultsByTitle(title string) ([]model.Vault, error)
} }
// Config holds the configuration for creating a new 1Password client.
type Config struct {
ConnectHost string
ConnectToken string
UserAgent string
ServiceAccountToken string
IntegrationName string
IntegrationVersion string
}
// NewClient creates a new 1Password client based on the provided configuration. // NewClient creates a new 1Password client based on the provided configuration.
func NewClient(config Config) (Client, error) { func NewClient(integrationVersion string) (Client, error) {
if config.ServiceAccountToken != "" { connectHost, _ := os.LookupEnv("OP_CONNECT_HOST")
connectToken, _ := os.LookupEnv("OP_CONNECT_TOKEN")
serviceAccountToken, _ := os.LookupEnv("OP_SERVICE_ACCOUNT_TOKEN")
if serviceAccountToken != "" {
return sdk.NewClient(sdk.Config{ return sdk.NewClient(sdk.Config{
ServiceAccountToken: config.ServiceAccountToken, ServiceAccountToken: serviceAccountToken,
IntegrationName: config.IntegrationName, IntegrationName: "1password-operator",
IntegrationVersion: config.IntegrationVersion, IntegrationVersion: integrationVersion,
}) })
} else if config.ConnectHost != "" && config.ConnectToken != "" { } else if connectHost != "" && connectToken != "" {
return connect.NewClient(connect.Config{ return connect.NewClient(connect.Config{
ConnectHost: config.ConnectHost, ConnectHost: connectHost,
ConnectToken: config.ConnectToken, ConnectToken: connectToken,
}), nil }), nil
} }
return nil, errors.New("invalid configuration. Either Connect or Service Account credentials should be set")
return nil, errors.New("invalid configuration. Connect or Service Account credentials should be set")
} }

View File

@@ -20,9 +20,10 @@ type Connect struct {
client connect.Client client connect.Client
} }
// NewClient creates a new Connect client using provided configuration.
func NewClient(config Config) *Connect { func NewClient(config Config) *Connect {
return &Connect{ return &Connect{
client: connect.NewClientWithUserAgent(config.ConnectHost, config.ConnectToken, config.UserAgent), client: connect.NewClient(config.ConnectHost, config.ConnectToken),
} }
} }

View File

@@ -4,36 +4,36 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/1Password/connect-sdk-go/connect"
"github.com/1Password/connect-sdk-go/onepassword"
logf "sigs.k8s.io/controller-runtime/pkg/log" logf "sigs.k8s.io/controller-runtime/pkg/log"
opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client"
"github.com/1Password/onepassword-operator/pkg/onepassword/model"
) )
var logger = logf.Log.WithName("retrieve_item") var logger = logf.Log.WithName("retrieve_item")
func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*onepassword.Item, error) { func GetOnePasswordItemByPath(opClient opclient.Client, path string) (*model.Item, error) {
vaultValue, itemValue, err := ParseVaultAndItemFromPath(path) vaultIdentifier, itemIdentifier, err := ParseVaultAndItemFromPath(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
vaultId, err := getVaultId(opConnectClient, vaultValue) vaultID, err := getVaultID(opClient, vaultIdentifier)
if err != nil { if err != nil {
return nil, err return nil, err
} }
itemId, err := getItemId(opConnectClient, itemValue, vaultId) itemID, err := getItemID(opClient, vaultID, itemIdentifier)
if err != nil { if err != nil {
return nil, err return nil, err
} }
item, err := opConnectClient.GetItem(itemId, vaultId) item, err := opClient.GetItemByID(itemID, vaultID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, file := range item.Files { for _, file := range item.Files {
_, err := opConnectClient.GetFileContent(file) _, err := opClient.GetFileContent(vaultID, itemID, file.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -50,7 +50,7 @@ func ParseVaultAndItemFromPath(path string) (string, string, error) {
return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path) return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path)
} }
func getVaultId(client connect.Client, vaultIdentifier string) (string, error) { func getVaultID(client opclient.Client, vaultIdentifier string) (string, error) {
if !IsValidClientUUID(vaultIdentifier) { if !IsValidClientUUID(vaultIdentifier) {
vaults, err := client.GetVaultsByTitle(vaultIdentifier) vaults, err := client.GetVaultsByTitle(vaultIdentifier)
if err != nil { if err != nil {
@@ -75,7 +75,7 @@ func getVaultId(client connect.Client, vaultIdentifier string) (string, error) {
return vaultIdentifier, nil return vaultIdentifier, nil
} }
func getItemId(client connect.Client, itemIdentifier string, vaultId string) (string, error) { func getItemID(client opclient.Client, vaultId, itemIdentifier string) (string, error) {
if !IsValidClientUUID(itemIdentifier) { if !IsValidClientUUID(itemIdentifier) {
items, err := client.GetItemsByTitle(itemIdentifier, vaultId) items, err := client.GetItemsByTitle(itemIdentifier, vaultId)
if err != nil { if err != nil {

View File

@@ -8,10 +8,10 @@ import (
onepasswordv1 "github.com/1Password/onepassword-operator/api/v1" onepasswordv1 "github.com/1Password/onepassword-operator/api/v1"
kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets" kubeSecrets "github.com/1Password/onepassword-operator/pkg/kubernetessecrets"
"github.com/1Password/onepassword-operator/pkg/logs" "github.com/1Password/onepassword-operator/pkg/logs"
opclient "github.com/1Password/onepassword-operator/pkg/onepassword/client"
"github.com/1Password/onepassword-operator/pkg/onepassword/model"
"github.com/1Password/onepassword-operator/pkg/utils" "github.com/1Password/onepassword-operator/pkg/utils"
"github.com/1Password/connect-sdk-go/connect"
"github.com/1Password/connect-sdk-go/onepassword"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@@ -23,17 +23,17 @@ const lockTag = "operator.1password.io:ignore-secret"
var log = logf.Log.WithName("update_op_kubernetes_secrets_task") var log = logf.Log.WithName("update_op_kubernetes_secrets_task")
func NewManager(kubernetesClient client.Client, opConnectClient connect.Client, shouldAutoRestartDeploymentsGlobal bool) *SecretUpdateHandler { func NewManager(kubernetesClient client.Client, opClient opclient.Client, shouldAutoRestartDeploymentsGlobal bool) *SecretUpdateHandler {
return &SecretUpdateHandler{ return &SecretUpdateHandler{
client: kubernetesClient, client: kubernetesClient,
opConnectClient: opConnectClient, opClient: opClient,
shouldAutoRestartDeploymentsGlobal: shouldAutoRestartDeploymentsGlobal, shouldAutoRestartDeploymentsGlobal: shouldAutoRestartDeploymentsGlobal,
} }
} }
type SecretUpdateHandler struct { type SecretUpdateHandler struct {
client client.Client client client.Client
opConnectClient connect.Client opClient opclient.Client
shouldAutoRestartDeploymentsGlobal bool shouldAutoRestartDeploymentsGlobal bool
} }
@@ -121,14 +121,14 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]*
OnePasswordItemPath := h.getPathFromOnePasswordItem(secret) OnePasswordItemPath := h.getPathFromOnePasswordItem(secret)
item, err := GetOnePasswordItemByPath(h.opConnectClient, OnePasswordItemPath) item, err := GetOnePasswordItemByPath(h.opClient, OnePasswordItemPath)
if err != nil { if err != nil {
log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name) log.Error(err, "failed to retrieve 1Password item at path \"%s\" for secret \"%s\"", secret.Annotations[ItemPathAnnotation], secret.Name)
continue continue
} }
itemVersion := fmt.Sprint(item.Version) itemVersion := fmt.Sprint(item.Version)
itemPathString := fmt.Sprintf("vaults/%v/items/%v", item.Vault.ID, item.ID) itemPathString := fmt.Sprintf("vaults/%v/items/%v", item.VaultID, item.ID)
if currentVersion != itemVersion || secret.Annotations[ItemPathAnnotation] != itemPathString { if currentVersion != itemVersion || secret.Annotations[ItemPathAnnotation] != itemPathString {
if isItemLockedForForcedRestarts(item) { if isItemLockedForForcedRestarts(item) {
@@ -159,7 +159,7 @@ func (h *SecretUpdateHandler) updateKubernetesSecrets() (map[string]map[string]*
return updatedSecrets, nil return updatedSecrets, nil
} }
func isItemLockedForForcedRestarts(item *onepassword.Item) bool { func isItemLockedForForcedRestarts(item *model.Item) bool {
tags := item.Tags tags := item.Tags
for i := 0; i < len(tags); i++ { for i := 0; i < len(tags); i++ {
if tags[i] == lockTag { if tags[i] == lockTag {