khuedoan-homelab/platform/global-secrets/files/secret-generator/main.go

141 lines
3.7 KiB
Go

package main
import (
"context"
"fmt"
"log"
"math"
"os"
"github.com/sethvargo/go-password/password"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
const namespace = "global-secrets"
type RandomSecret struct {
Name string
Data []struct {
Key string
Length int
Special bool
}
}
func getClient() (*kubernetes.Clientset, error) {
rules := clientcmd.NewDefaultClientConfigLoadingRules()
overrides := &clientcmd.ConfigOverrides{}
config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides).ClientConfig()
if err != nil {
return nil, fmt.Errorf("Error building client config: %v", err)
}
return kubernetes.NewForConfig(config)
}
func generateRandomPassword(length int, special bool) (string, error) {
numDigits := int(math.Ceil(float64(length) * 0.2))
numSymbols := 0
if special {
numSymbols = int(math.Ceil(float64(length) * 0.2))
}
return password.Generate(length, numDigits, numSymbols, false, true)
}
func readConfigFile(filename string) ([]RandomSecret, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("Unable to read config file: %v", err)
}
var randomSecrets []RandomSecret
err = yaml.Unmarshal(data, &randomSecrets)
if err != nil {
return nil, fmt.Errorf("Error parsing config file: %v", err)
}
return randomSecrets, nil
}
func createOrUpdateSecret(client *kubernetes.Clientset, name string, randomSecret RandomSecret) error {
secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
// Secret not found, create a new one
secretData := map[string][]byte{}
for _, randomPassword := range randomSecret.Data {
password, err := generateRandomPassword(randomPassword.Length, randomPassword.Special)
if err != nil {
log.Printf("Error generating password for key '%s': %v", randomPassword.Key, err)
continue
}
secretData[randomPassword.Key] = []byte(password)
}
newSecret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Data: secretData,
}
_, err := client.CoreV1().Secrets(namespace).Create(context.Background(), newSecret, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("Unable to create secret: %v", err)
}
log.Printf("Secret '%s' created successfully.", name)
} else {
// Secret exists, check for new keys
for _, randomKey := range randomSecret.Data {
if _, exists := secret.Data[randomKey.Key]; !exists {
// New key found, generate new password
log.Printf("New key '%s' found in config for secret '%s', generating new password", randomKey.Key, name)
password, err := generateRandomPassword(randomKey.Length, randomKey.Special)
if err != nil {
log.Printf("Error generating password for key '%s': %v", randomKey.Key, err)
continue
}
secret.Data[randomKey.Key] = []byte(password)
}
}
// Update the secret
_, err := client.CoreV1().Secrets(namespace).Update(context.Background(), secret, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("Unable to update secret: %v", err)
}
}
return nil
}
func main() {
configFilename := "./config.yaml"
randomSecrets, err := readConfigFile(configFilename)
if err != nil {
log.Fatalf("Error reading config file: %v", err)
}
client, err := getClient()
if err != nil {
log.Fatalf("Unable to create Kubernetes client: %v", err)
}
for _, randomSecret := range randomSecrets {
err := createOrUpdateSecret(client, randomSecret.Name, randomSecret)
if err != nil {
log.Printf("Error processing secret %s: %v", randomSecret.Name, err)
}
}
}