package main import ( "context" "fmt" "os" "os/user" "time" "git.cloud.cluster.fun/AverageMarcus/kube-1password-secrets/internal/onepassword" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) const ( idAnnotation = "kube-1password" vaultAnnotation = "kube-1password/vault" usernameAnnotation = "kube-1password/username-key" passwordAnnotation = "kube-1password/password-key" secretTextAnnotation = "kube-1password/secret-text-key" ) func main() { opClient, err := buildOpClient() if err != nil { panic(err.Error()) } config, err := rest.InClusterConfig() if err != nil { panic(err.Error()) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err) } for { 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 { keys := parseAnnotations(s.ObjectMeta.Annotations) vault := keys["vault"] item, err := opClient.GetSecret(vault, passwordID) if err != nil { fmt.Println("[ERROR] Could not get secret", err) continue } s.Data = make(map[string][]byte) if item.Username != "" { s.Data[keys["username"]] = []byte(item.Username) } if item.Password != "" { s.Data[keys["password"]] = []byte(item.Password) } if item.SecretText != "" { s.Data[keys["secretText"]] = []byte(item.SecretText) } if _, err := clientset.CoreV1().Secrets(s.GetNamespace()).Update(context.Background(), &s, metav1.UpdateOptions{}); err != nil { fmt.Println("[ERROR] Could not update secret value", err) continue } } } time.Sleep(5 * time.Minute) } } func buildOpClient() (*onepassword.Client, error) { usr, _ := user.Current() err := os.Chmod(usr.HomeDir+"/.op", 0700) if err != nil { panic(err.Error()) } domain, ok := os.LookupEnv("OP_DOMAIN") if !ok { return nil, fmt.Errorf("OP_DOMAIN not specified") } email, ok := os.LookupEnv("OP_EMAIL") if !ok { return nil, fmt.Errorf("OP_EMAIL not specified") } password, ok := os.LookupEnv("OP_PASSWORD") if !ok { return nil, fmt.Errorf("OP_PASSWORD not specified") } secretKey, ok := os.LookupEnv("OP_SECRET_KEY") if !ok { return nil, fmt.Errorf("OP_SECRET_KEY not specified") } return onepassword.New(domain, email, password, secretKey) } func parseAnnotations(annotations map[string]string) map[string]string { keys := map[string]string{ "username": "username", "password": "password", "secretText": "secretText", "vault": os.Getenv("OP_VAULT"), } for k, v := range annotations { switch k { case vaultAnnotation: keys["vault"] = v case usernameAnnotation: keys["username"] = v case passwordAnnotation: keys["password"] = v case secretTextAnnotation: keys["secretText"] = v } } return keys }