mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 04:40:05 +01:00
feat: sdk migration in progress
This commit is contained in:
83
cmd/encryption.go
Normal file
83
cmd/encryption.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/glueops/autoglue/internal/app"
|
||||
"github.com/glueops/autoglue/internal/models"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rotateMasterCmd = &cobra.Command{
|
||||
Use: "rotate-master",
|
||||
Short: "Generate and activate a new master encryption key",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
rt := app.NewRuntime()
|
||||
db := rt.DB
|
||||
|
||||
key := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
return fmt.Errorf("generating random key: %w", err)
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
if err := db.Model(&models.MasterKey{}).
|
||||
Where("is_active = ?", true).
|
||||
Update("is_active", false).Error; err != nil {
|
||||
return fmt.Errorf("deactivating previous key: %w", err)
|
||||
}
|
||||
|
||||
if err := db.Create(&models.MasterKey{
|
||||
Key: encoded,
|
||||
IsActive: true,
|
||||
}).Error; err != nil {
|
||||
return fmt.Errorf("creating new master key: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Master key rotated successfully")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var createMasterCmd = &cobra.Command{
|
||||
Use: "create-master",
|
||||
Short: "Generate and activate a new master encryption key",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
rt := app.NewRuntime()
|
||||
db := rt.DB
|
||||
key := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
return fmt.Errorf("generating random key: %w", err)
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(key)
|
||||
|
||||
if err := db.Create(&models.MasterKey{
|
||||
Key: encoded,
|
||||
IsActive: true,
|
||||
}).Error; err != nil {
|
||||
return fmt.Errorf("creating master key: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Master key created successfully")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var encryptCmd = &cobra.Command{
|
||||
Use: "encrypt",
|
||||
Short: "Manage autoglue encryption keys",
|
||||
Long: "Manage autoglue master encryption keys used for securing data.",
|
||||
}
|
||||
|
||||
func init() {
|
||||
encryptCmd.AddCommand(rotateMasterCmd)
|
||||
encryptCmd.AddCommand(createMasterCmd)
|
||||
rootCmd.AddCommand(encryptCmd)
|
||||
}
|
||||
81
cmd/keys_generate.go
Normal file
81
cmd/keys_generate.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/app"
|
||||
"github.com/glueops/autoglue/internal/keys"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
alg string
|
||||
rsaBits int
|
||||
kidFlag string
|
||||
nbfStr string
|
||||
expStr string
|
||||
)
|
||||
|
||||
var keysCmd = &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: "Manage JWT signing keys",
|
||||
}
|
||||
|
||||
var keysGenCmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "Generate and store a new signing key",
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
rt := app.NewRuntime()
|
||||
|
||||
var nbfPtr, expPtr *time.Time
|
||||
if nbfStr != "" {
|
||||
t, err := time.Parse(time.RFC3339, nbfStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nbfPtr = &t
|
||||
}
|
||||
if expStr != "" {
|
||||
t, err := time.Parse(time.RFC3339, expStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expPtr = &t
|
||||
}
|
||||
|
||||
rec, err := keys.GenerateAndStore(rt.DB, rt.Cfg.JWTPrivateEncKey, keys.GenOpts{
|
||||
Alg: alg,
|
||||
Bits: rsaBits,
|
||||
KID: kidFlag,
|
||||
NBF: nbfPtr,
|
||||
EXP: expPtr,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("created signing key\n")
|
||||
fmt.Printf(" kid: %s\n", rec.Kid)
|
||||
fmt.Printf(" alg: %s\n", rec.Alg)
|
||||
fmt.Printf(" active: %v\n", rec.IsActive)
|
||||
if rec.NotBefore != nil {
|
||||
fmt.Printf(" nbf: %s\n", rec.NotBefore.Format(time.RFC3339))
|
||||
}
|
||||
if rec.ExpiresAt != nil {
|
||||
fmt.Printf(" exp: %s\n", rec.ExpiresAt.Format(time.RFC3339))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(keysCmd)
|
||||
keysCmd.AddCommand(keysGenCmd)
|
||||
|
||||
keysGenCmd.Flags().StringVarP(&alg, "alg", "a", "EdDSA", "Signing alg: EdDSA|RS256|RS384|RS512")
|
||||
keysGenCmd.Flags().IntVarP(&rsaBits, "bits", "b", 3072, "RSA key size (when alg is RS*)")
|
||||
keysGenCmd.Flags().StringVarP(&kidFlag, "kid", "k", "", "Key ID (optional; auto if empty)")
|
||||
keysGenCmd.Flags().StringVarP(&nbfStr, "nbf", "n", "", "Not Before (RFC3339)")
|
||||
keysGenCmd.Flags().StringVarP(&expStr, "exp", "e", "", "Expires At (RFC3339)")
|
||||
}
|
||||
33
cmd/root.go
Normal file
33
cmd/root.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "autoglue",
|
||||
Short: "Autoglue Kubernetes Cluster Management",
|
||||
Long: "autoglue is used to manage the lifecycle of kubernetes clusters on GlueOps supported cloud providers",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
err := serveCmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
_ = cmd.Help()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize()
|
||||
}
|
||||
94
cmd/serve.go
Normal file
94
cmd/serve.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/glueops/autoglue/internal/api"
|
||||
"github.com/glueops/autoglue/internal/app"
|
||||
"github.com/glueops/autoglue/internal/auth"
|
||||
"github.com/glueops/autoglue/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Start API server",
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
rt := app.NewRuntime()
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = auth.Refresh(rt.DB, rt.Cfg.JWTPrivateEncKey)
|
||||
go func() {
|
||||
t := time.NewTicker(60 * time.Second)
|
||||
defer t.Stop()
|
||||
for range t.C {
|
||||
_ = auth.Refresh(rt.DB, rt.Cfg.JWTPrivateEncKey)
|
||||
}
|
||||
}()
|
||||
|
||||
r := api.NewRouter(rt.DB)
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", cfg.Host, cfg.Port)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: TimeoutExceptUpgrades(r, 60*time.Second, "request timed out"), // global safety
|
||||
ReadTimeout: 15 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
go func() {
|
||||
fmt.Printf("🚀 API running on http://%s (ui.dev=%v)\n", addr, cfg.UIDev)
|
||||
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
fmt.Println("\n⏳ Shutting down...")
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
return srv.Shutdown(shutdownCtx)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
}
|
||||
|
||||
func TimeoutExceptUpgrades(next http.Handler, d time.Duration, msg string) http.Handler {
|
||||
timeout := http.TimeoutHandler(next, d, msg)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// If this is an upgrade (e.g., websocket), don't wrap.
|
||||
if isUpgrade(r) {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
timeout.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func isUpgrade(r *http.Request) bool {
|
||||
// Connection: Upgrade, Upgrade: websocket
|
||||
if strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
20
cmd/version.go
Normal file
20
cmd/version.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/glueops/autoglue/internal/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(version.Info())
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user