From 4bb3b58ddbb7859351c423e2a94a69e779d3b75f Mon Sep 17 00:00:00 2001 From: H3ALY Date: Wed, 16 Apr 2025 08:21:02 +0100 Subject: [PATCH] Bootstrapped the creation and loading of session keys to lighten main. --- bootstrap/session.go | 61 +++++++++++++++++++++++++++++++++++ handlers/security/session.go | 23 ++++++++----- helpers/session/encoding.go | 7 ++++ internal/sessionkeys/auth.key | 1 + internal/sessionkeys/enc.key | 1 + logging/config.go | 4 +-- main.go | 5 +++ models/config.go | 6 ++-- 8 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 bootstrap/session.go create mode 100644 helpers/session/encoding.go create mode 100644 internal/sessionkeys/auth.key create mode 100644 internal/sessionkeys/enc.key diff --git a/bootstrap/session.go b/bootstrap/session.go new file mode 100644 index 0000000..6edaed1 --- /dev/null +++ b/bootstrap/session.go @@ -0,0 +1,61 @@ +package bootstrap + +import ( + "crypto/rand" + "os" + + securityhandlers "synlotto-website/handlers/security" + + helpers "synlotto-website/helpers/session" + "synlotto-website/logging" + "synlotto-website/models" +) + +func InitSession(cfg *models.Config) error { + authPath := cfg.Session.AuthKeyPath + encPath := cfg.Session.EncryptionKeyPath + + if _, err := os.Stat(authPath); os.IsNotExist(err) { + logging.Info("⚠️ Auth key not found, creating: %s", authPath) + key, err := generateRandomBytes(32) + if err != nil { + return err + } + encoded := helpers.EncodeKey(key) + err = os.WriteFile(authPath, []byte(encoded), 0600) + if err != nil { + return err + } + } + + if _, err := os.Stat(encPath); os.IsNotExist(err) { + logging.Info("⚠️ Encryption key not found, creating: %s", encPath) + key, err := generateRandomBytes(32) + if err != nil { + return err + } + encoded := helpers.EncodeKey(key) + err = os.WriteFile(encPath, []byte(encoded), 0600) + if err != nil { + return err + } + } + + return securityhandlers.LoadSessionKeys( + authPath, + encPath, + cfg.Session.Name, + cfg.HttpServer.ProductionMode, + ) +} + +func generateRandomBytes(length int) ([]byte, error) { + b := make([]byte, length) + _, err := rand.Read(b) + if err != nil { + logging.Error("failed to generate random bytes: %w", err) + + return nil, err + } + return b, nil +} diff --git a/handlers/security/session.go b/handlers/security/session.go index 9c1677a..ccd1d51 100644 --- a/handlers/security/session.go +++ b/handlers/security/session.go @@ -2,6 +2,7 @@ package security import ( "bytes" + "encoding/base64" "encoding/gob" "fmt" "net/http" @@ -25,21 +26,27 @@ func init() { func LoadSessionKeys(authPath, encryptionPath, name string, isProduction bool) error { var err error - authKey, err = os.ReadFile(authPath) + + rawAuth, err := os.ReadFile(authPath) if err != nil { - return fmt.Errorf("error loading auth key: %w", err) + return fmt.Errorf("error reading auth key: %w", err) + } + authKey, err = base64.StdEncoding.DecodeString(string(bytes.TrimSpace(rawAuth))) + if err != nil { + return fmt.Errorf("error decoding auth key: %w", err) } - encryptKey, err = os.ReadFile(encryptionPath) + rawEnc, err := os.ReadFile(encryptionPath) if err != nil { - return fmt.Errorf("error loading encryption key: %w", err) + return fmt.Errorf("error reading encryption key: %w", err) + } + encryptKey, err = base64.StdEncoding.DecodeString(string(bytes.TrimSpace(rawEnc))) + if err != nil { + return fmt.Errorf("error decoding encryption key: %w", err) } - - authKey = bytes.TrimSpace(authKey) - encryptKey = bytes.TrimSpace(encryptKey) if len(authKey) != 32 || len(encryptKey) != 32 { - return fmt.Errorf("auth and encryption keys must be 32 bytes each") + return fmt.Errorf("auth and encryption keys must be 32 bytes each (got auth=%d, enc=%d)", len(authKey), len(encryptKey)) } sessionStore = sessions.NewCookieStore(authKey, encryptKey) diff --git a/helpers/session/encoding.go b/helpers/session/encoding.go new file mode 100644 index 0000000..401ff9a --- /dev/null +++ b/helpers/session/encoding.go @@ -0,0 +1,7 @@ +package helpers + +import "encoding/base64" + +func EncodeKey(b []byte) string { + return base64.StdEncoding.EncodeToString(b) +} diff --git a/internal/sessionkeys/auth.key b/internal/sessionkeys/auth.key new file mode 100644 index 0000000..19b818a --- /dev/null +++ b/internal/sessionkeys/auth.key @@ -0,0 +1 @@ +eR/+k+Oc6xqi0Petbxgf282UXQiuNz99mx/yak3+Ekc= \ No newline at end of file diff --git a/internal/sessionkeys/enc.key b/internal/sessionkeys/enc.key new file mode 100644 index 0000000..a55b1c1 --- /dev/null +++ b/internal/sessionkeys/enc.key @@ -0,0 +1 @@ +1wYNu1oHCCG8vR35vperPFSyz/a2bq2nMddEnUd1lOI= \ No newline at end of file diff --git a/logging/config.go b/logging/config.go index c4b1022..58f76a8 100644 --- a/logging/config.go +++ b/logging/config.go @@ -10,8 +10,8 @@ import ( func LogConfig(config *models.Config) { safeConfig := *config safeConfig.CSRF.CSRFKey = "[REDACTED]" - safeConfig.Session.SessionAuthKey = "[REDACTED]" - safeConfig.Session.SessionEncryptionKey = "[REDACTED]" + safeConfig.Session.AuthKeyPath = "[REDACTED]" + safeConfig.Session.EncryptionKeyPath = "[REDACTED]" cfg, err := json.MarshalIndent(safeConfig, "", " ") if err != nil { diff --git a/main.go b/main.go index e446e74..7cf1d57 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,11 @@ func main() { db := storage.InitDB("synlotto.db") models.SetDB(db) // Should be in storage not models. + err = bootstrap.InitSession(appState.Config) + if err != nil { + logging.Error("❌ Failed to init session: %v", err) + } + err = securityhandlers.InitCSRFProtection([]byte(appState.Config.CSRF.CSRFKey), appState.Config.HttpServer.ProductionMode) if err != nil { logging.Error("Failed to init CSRF: %v", err) diff --git a/models/config.go b/models/config.go index 5659594..91743e8 100644 --- a/models/config.go +++ b/models/config.go @@ -12,8 +12,8 @@ type Config struct { } `json:"csrf"` Session struct { - SessionAuthKey string `json:"authKey"` - SessionEncryptionKey string `json:"encryptionKey"` - SessionName string `json:"sessionName"` + AuthKeyPath string `json:"authKeyPath"` + EncryptionKeyPath string `json:"encryptionKeyPath"` + Name string `json:"sessionName"` } `json:"session"` }