Updated to use informers

Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
master
Marcus Noble 4 months ago
parent b5dda17f66
commit dcd698c60d
Signed by: AverageMarcus
GPG Key ID: B8F2DB8A7AEBAF78
  1. 2
      Makefile
  2. 6
      go.sum
  3. 122
      main.go
  4. 2
      manifests/rbac.yaml

@ -1,6 +1,6 @@
.DEFAULT_GOAL := default
IMAGE ?= docker.cluster.fun/averagemarcus/kube-1password-secrets:latest
IMAGE ?= rg.fr-par.scw.cloud/averagemarcus/kube-1password-secrets:latest
.PHONY: test # Run all tests, linting and format checks
test: lint check-format run-tests

@ -23,6 +23,7 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
@ -31,6 +32,7 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -55,6 +57,7 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
@ -63,6 +66,7 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -160,7 +164,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -198,6 +201,7 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=

@ -7,16 +7,21 @@ import (
"os"
"os/user"
"strings"
"time"
"git.cloud.cluster.fun/AverageMarcus/kube-1password-secrets/internal/onepassword"
apiv1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
)
const (
fieldManagerName = "kube-1password-secrets"
idAnnotation = "kube-1password"
vaultAnnotation = "kube-1password/vault"
usernameAnnotation = "kube-1password/username-key"
@ -25,8 +30,14 @@ const (
secretTextParseAnnotation = "kube-1password/secret-text-parse"
)
var (
opClient *onepassword.Client
clientset *kubernetes.Clientset
)
func main() {
opClient, err := buildOpClient()
var err error
opClient, err = buildOpClient()
if err != nil {
panic(err.Error())
}
@ -35,63 +46,86 @@ func main() {
if err != nil {
panic(err.Error())
}
clientset, err := kubernetes.NewForConfig(config)
clientset, err = kubernetes.NewForConfig(config)
if err != nil {
panic(err)
}
for {
log.Println("[DEBUG] Syncing secrets")
list, err := clientset.CoreV1().Secrets(apiv1.NamespaceAll).List(context.Background(), metav1.ListOptions{})
if err != nil {
panic(err)
}
for _, s := range list.Items {
if passwordID, exists := s.ObjectMeta.Annotations[idAnnotation]; exists {
log.Printf("[INFO] Syncing secret %s with 1Password secret %s\n", s.GetName(), passwordID)
keys := parseAnnotations(s.ObjectMeta.Annotations)
stopper := make(chan struct{})
defer close(stopper)
factory := informers.NewSharedInformerFactory(clientset, 0)
secretInformer := factory.Core().V1().Secrets()
informer := secretInformer.Informer()
defer runtime.HandleCrash()
go factory.Start(stopper)
if !cache.WaitForCacheSync(stopper, informer.HasSynced) {
runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
return
}
vault := keys["vault"]
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) { processSecret(obj.(*apiv1.Secret)) },
UpdateFunc: func(old interface{}, new interface{}) {
managedFields := new.(*apiv1.Secret).GetManagedFields()
if len(managedFields) == 0 || managedFields[len(managedFields)-1].Manager != fieldManagerName {
processSecret(new.(*apiv1.Secret))
}
},
DeleteFunc: func(interface{}) {},
})
item, err := opClient.GetSecret(vault, passwordID)
if err != nil {
log.Println("[ERROR] Could not get secret", err)
continue
}
lister := secretInformer.Lister().Secrets((v1.NamespaceAll))
secrets, err := lister.List(labels.Everything())
s.Data = make(map[string][]byte)
for _, s := range secrets {
processSecret(s)
}
if item.Username != "" {
s.Data[keys["username"]] = []byte(parseNewlines(item.Username))
}
<-stopper
}
if item.Password != "" {
s.Data[keys["password"]] = []byte(parseNewlines(item.Password))
}
func processSecret(s *apiv1.Secret) {
if passwordID, exists := s.ObjectMeta.Annotations[idAnnotation]; exists {
log.Printf("[INFO] Syncing secret %s with 1Password secret %s\n", s.GetName(), passwordID)
keys := parseAnnotations(s.ObjectMeta.Annotations)
if item.SecretText != "" {
if s.ObjectMeta.Annotations[secretTextParseAnnotation] != "" {
// Parse secret text as individual secrets
lines := strings.Split(item.SecretText, "\n")
for _, line := range lines {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
s.Data[parts[0]] = []byte(parseNewlines(parts[1]))
}
}
} else {
s.Data[keys["secretText"]] = []byte(parseNewlines(item.SecretText))
}
}
vault := keys["vault"]
item, err := opClient.GetSecret(vault, passwordID)
if err != nil {
log.Println("[ERROR] Could not get secret", err)
return
}
s.Data = make(map[string][]byte)
if item.Username != "" {
s.Data[keys["username"]] = []byte(parseNewlines(item.Username))
}
if item.Password != "" {
s.Data[keys["password"]] = []byte(parseNewlines(item.Password))
}
if _, err := clientset.CoreV1().Secrets(s.GetNamespace()).Update(context.Background(), &s, metav1.UpdateOptions{}); err != nil {
log.Println("[ERROR] Could not update secret value", err)
continue
if item.SecretText != "" {
if s.ObjectMeta.Annotations[secretTextParseAnnotation] != "" {
// Parse secret text as individual secrets
lines := strings.Split(item.SecretText, "\n")
for _, line := range lines {
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
s.Data[parts[0]] = []byte(parseNewlines(parts[1]))
}
}
} else {
s.Data[keys["secretText"]] = []byte(parseNewlines(item.SecretText))
}
}
time.Sleep(5 * time.Minute)
_, err = clientset.CoreV1().Secrets(s.GetNamespace()).Update(context.Background(), s, metav1.UpdateOptions{FieldManager: fieldManagerName})
if err != nil {
log.Println("[ERROR] Could not update secret value", err)
}
}
}

@ -12,7 +12,7 @@ metadata:
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "update"]
verbs: ["get", "list", "update", "watch"]
---

Loading…
Cancel
Save