refactor: auto-generate session key and store in database
All checks were successful
check / check (push) Successful in 57s
All checks were successful
check / check (push) Successful in 57s
Remove SESSION_KEY env var requirement. On first startup, a cryptographically secure 32-byte key is generated and stored in a new settings table. Subsequent startups load the key from the database. - Add Setting model (key-value table) for application config - Add Database.GetOrCreateSessionKey() method - Session manager initializes in OnStart after database is connected - Remove DevSessionKey constant and SESSION_KEY env var handling - Remove prod validation requiring SESSION_KEY - Update README: config table, Docker instructions, security notes - Update config.yaml.example - Update all tests to remove SessionKey references Addresses owner feedback on issue #15.
This commit is contained in:
@@ -2,7 +2,11 @@ package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"go.uber.org/fx"
|
||||
@@ -142,3 +146,35 @@ func (d *Database) close() error {
|
||||
func (d *Database) DB() *gorm.DB {
|
||||
return d.db
|
||||
}
|
||||
|
||||
// GetOrCreateSessionKey retrieves the session encryption key from the
|
||||
// settings table. If no key exists, a cryptographically secure random
|
||||
// 32-byte key is generated, base64-encoded, and stored for future use.
|
||||
func (d *Database) GetOrCreateSessionKey() (string, error) {
|
||||
var setting Setting
|
||||
result := d.db.Where(&Setting{Key: "session_key"}).First(&setting)
|
||||
if result.Error == nil {
|
||||
return setting.Value, nil
|
||||
}
|
||||
if !errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
return "", fmt.Errorf("failed to query session key: %w", result.Error)
|
||||
}
|
||||
|
||||
// Generate a new cryptographically secure 32-byte key
|
||||
keyBytes := make([]byte, 32)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("failed to generate session key: %w", err)
|
||||
}
|
||||
encoded := base64.StdEncoding.EncodeToString(keyBytes)
|
||||
|
||||
setting = Setting{
|
||||
Key: "session_key",
|
||||
Value: encoded,
|
||||
}
|
||||
if err := d.db.Create(&setting).Error; err != nil {
|
||||
return "", fmt.Errorf("failed to store session key: %w", err)
|
||||
}
|
||||
|
||||
d.log.Info("generated new session key and stored in database")
|
||||
return encoded, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ environments:
|
||||
environment: dev
|
||||
dburl: "file::memory:?cache=shared"
|
||||
secrets:
|
||||
sessionKey: d2ViaG9va2VyLWRldi1zZXNzaW9uLWtleS1pbnNlY3VyZSE=
|
||||
sentryDSN: ""
|
||||
configDefaults:
|
||||
port: 8080
|
||||
|
||||
8
internal/database/model_setting.go
Normal file
8
internal/database/model_setting.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package database
|
||||
|
||||
// Setting stores application-level key-value configuration.
|
||||
// Used for auto-generated values like the session encryption key.
|
||||
type Setting struct {
|
||||
Key string `gorm:"primaryKey" json:"key"`
|
||||
Value string `gorm:"type:text;not null" json:"value"`
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package database
|
||||
// per-webhook dedicated databases managed by WebhookDBManager.
|
||||
func (d *Database) Migrate() error {
|
||||
return d.db.AutoMigrate(
|
||||
&Setting{},
|
||||
&User{},
|
||||
&APIKey{},
|
||||
&Webhook{},
|
||||
|
||||
@@ -28,8 +28,6 @@ environments:
|
||||
port: 8080
|
||||
debug: false
|
||||
dburl: "file::memory:?cache=shared"
|
||||
secrets:
|
||||
sessionKey: d2ViaG9va2VyLWRldi1zZXNzaW9uLWtleS1pbnNlY3VyZSE=
|
||||
configDefaults:
|
||||
port: 8080
|
||||
`
|
||||
@@ -51,9 +49,8 @@ configDefaults:
|
||||
dataDir := filepath.Join(t.TempDir(), "events")
|
||||
|
||||
cfg := &config.Config{
|
||||
DBURL: "file::memory:?cache=shared",
|
||||
DataDir: dataDir,
|
||||
SessionKey: "d2ViaG9va2VyLWRldi1zZXNzaW9uLWtleS1pbnNlY3VyZSE=",
|
||||
DBURL: "file::memory:?cache=shared",
|
||||
DataDir: dataDir,
|
||||
}
|
||||
_ = cfg
|
||||
|
||||
|
||||
Reference in New Issue
Block a user