mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-09 16:50:01 +00:00
[core] Add validation and reset of encrypted values
in order to detect change of encryption pass Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
7126f6d7b7
commit
3213f4ac37
4 changed files with 80 additions and 0 deletions
|
@ -209,6 +209,13 @@ func (s Service) ListPermittedChannels() ([]string, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (s Service) RemoveAllExtendedTwitchCredentials() error {
|
||||
return errors.Wrap(
|
||||
s.db.DB().Delete(&extendedPermission{}, "1 = 1").Error,
|
||||
"deleting data from table",
|
||||
)
|
||||
}
|
||||
|
||||
func (s Service) RemoveExendedTwitchCredentials(channel string) error {
|
||||
return errors.Wrap(
|
||||
s.db.DB().Delete(&extendedPermission{}, "channel = ?", strings.TrimLeft(channel, "#")).Error,
|
||||
|
|
17
main.go
17
main.go
|
@ -198,6 +198,7 @@ func handleSubCommand(args []string) {
|
|||
fmt.Println(" actor-docs Generate markdown documentation for available actors")
|
||||
fmt.Println(" api-token <name> <scope...> Generate an api-token to be entered into the config")
|
||||
fmt.Println(" migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database")
|
||||
fmt.Println(" reset-secrets Remove encrypted data to reset encryption passphrase")
|
||||
fmt.Println(" validate-config Try to load configuration file and report errors if any")
|
||||
fmt.Println(" help Prints this help message")
|
||||
|
||||
|
@ -217,6 +218,18 @@ func handleSubCommand(args []string) {
|
|||
|
||||
log.Info("v2 storage file was migrated")
|
||||
|
||||
case "reset-secrets":
|
||||
// Nuke permission table entries
|
||||
if err := accessService.RemoveAllExtendedTwitchCredentials(); err != nil {
|
||||
log.WithError(err).Fatal("resetting Twitch credentials")
|
||||
}
|
||||
log.Info("removed stored Twitch credentials")
|
||||
|
||||
if err := db.ResetEncryptedCoreMeta(); err != nil {
|
||||
log.WithError(err).Fatal("resetting encrypted meta entries")
|
||||
}
|
||||
log.Info("removed encrypted meta entries")
|
||||
|
||||
case "validate-config":
|
||||
if err := loadConfig(cfg.Config); err != nil {
|
||||
log.WithError(err).Fatal("loading config")
|
||||
|
@ -303,6 +316,10 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
if err = db.ValidateEncryption(); err != nil {
|
||||
log.WithError(err).Fatal("validation of database encryption failed, fix encryption passphrase or use 'twitch-bot reset-secrets' to wipe encrypted data")
|
||||
}
|
||||
|
||||
if err = loadConfig(cfg.Config); err != nil {
|
||||
if os.IsNotExist(errors.Cause(err)) {
|
||||
if err = writeDefaultConfigFile(cfg.Config); err != nil {
|
||||
|
|
|
@ -2,12 +2,23 @@ package database
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"github.com/Luzifer/go_helpers/v2/backoff"
|
||||
)
|
||||
|
||||
const (
|
||||
encryptionValidationKey = "encryption-validation"
|
||||
encryptionValidationMinBackoff = 500 * time.Millisecond
|
||||
encryptionValidationTries = 5
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -47,12 +58,55 @@ func (c connector) ReadEncryptedCoreMeta(key string, value any) error {
|
|||
return c.readCoreMeta(key, value, c.DecryptField)
|
||||
}
|
||||
|
||||
// ResetEncryptedCoreMeta removes all CoreKV entries from the database
|
||||
func (c connector) ResetEncryptedCoreMeta() error {
|
||||
return errors.Wrap(
|
||||
c.db.Delete(&coreKV{}, "value LIKE ?", "U2FsdGVkX1%").Error,
|
||||
"removing encrypted meta entries",
|
||||
)
|
||||
}
|
||||
|
||||
// StoreEncryptedCoreMeta works like StoreCoreMeta but encrypts the
|
||||
// marshalled value before storing it
|
||||
func (c connector) StoreEncryptedCoreMeta(key string, value any) error {
|
||||
return c.storeCoreMeta(key, value, c.EncryptField)
|
||||
}
|
||||
|
||||
func (c connector) ValidateEncryption() error {
|
||||
validationHasher := sha512.New()
|
||||
fmt.Fprint(validationHasher, c.encryptionSecret)
|
||||
|
||||
var (
|
||||
storedHash string
|
||||
validationHash = fmt.Sprintf("%x", validationHasher.Sum(nil))
|
||||
)
|
||||
|
||||
err := backoff.NewBackoff().
|
||||
WithMaxIterations(encryptionValidationTries).
|
||||
WithMinIterationTime(encryptionValidationMinBackoff).
|
||||
Retry(func() error {
|
||||
return c.ReadEncryptedCoreMeta(encryptionValidationKey, &storedHash)
|
||||
})
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
if storedHash != validationHash {
|
||||
// Shouldn't happen: When decryption is possible it should match
|
||||
return errors.New("mismatch between expected and stored hash")
|
||||
}
|
||||
return nil
|
||||
|
||||
case errors.Is(err, ErrCoreMetaNotFound):
|
||||
return errors.Wrap(
|
||||
c.StoreEncryptedCoreMeta(encryptionValidationKey, validationHash),
|
||||
"initializing encryption validation",
|
||||
)
|
||||
|
||||
default:
|
||||
return errors.Wrap(err, "reading encryption-validation")
|
||||
}
|
||||
}
|
||||
|
||||
func (c connector) readCoreMeta(key string, value any, processor func(string) (string, error)) (err error) {
|
||||
var data coreKV
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@ type (
|
|||
ReadCoreMeta(key string, value any) error
|
||||
StoreCoreMeta(key string, value any) error
|
||||
ReadEncryptedCoreMeta(key string, value any) error
|
||||
ResetEncryptedCoreMeta() error
|
||||
StoreEncryptedCoreMeta(key string, value any) error
|
||||
DecryptField(string) (string, error)
|
||||
EncryptField(string) (string, error)
|
||||
ValidateEncryption() error
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue