Updated to use informers
Signed-off-by: Marcus Noble <github@marcusnoble.co.uk>
This commit is contained in:
parent
b5dda17f66
commit
dcd698c60d
2
Makefile
2
Makefile
@ -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
|
||||
|
6
go.sum
6
go.sum
@ -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=
|
||||
|
122
main.go
122
main.go
@ -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{})
|
||||
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
|
||||
}
|
||||
|
||||
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{}) {},
|
||||
})
|
||||
|
||||
lister := secretInformer.Lister().Secrets((v1.NamespaceAll))
|
||||
secrets, err := lister.List(labels.Everything())
|
||||
|
||||
for _, s := range secrets {
|
||||
processSecret(s)
|
||||
}
|
||||
|
||||
<-stopper
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
vault := keys["vault"]
|
||||
|
||||
item, err := opClient.GetSecret(vault, passwordID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
log.Println("[ERROR] Could not get secret", err)
|
||||
return
|
||||
}
|
||||
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)
|
||||
|
||||
vault := keys["vault"]
|
||||
s.Data = make(map[string][]byte)
|
||||
|
||||
item, err := opClient.GetSecret(vault, passwordID)
|
||||
if err != nil {
|
||||
log.Println("[ERROR] Could not get secret", err)
|
||||
continue
|
||||
}
|
||||
if item.Username != "" {
|
||||
s.Data[keys["username"]] = []byte(parseNewlines(item.Username))
|
||||
}
|
||||
|
||||
s.Data = make(map[string][]byte)
|
||||
if item.Password != "" {
|
||||
s.Data[keys["password"]] = []byte(parseNewlines(item.Password))
|
||||
}
|
||||
|
||||
if item.Username != "" {
|
||||
s.Data[keys["username"]] = []byte(parseNewlines(item.Username))
|
||||
}
|
||||
|
||||
if item.Password != "" {
|
||||
s.Data[keys["password"]] = []byte(parseNewlines(item.Password))
|
||||
}
|
||||
|
||||
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))
|
||||
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]))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
} 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…
Reference in New Issue
Block a user