feat: encrypt sensitive values in app.yaml with AES-256-GCM
Passwords and secrets from deploy fields (type: password/secret) are now encrypted at rest in app.yaml using a per-node 32-byte key. Values stored as ENC:base64(nonce+ciphertext), decrypted transparently for docker-compose and web UI. Key included in infra backup bundle for disaster recovery. Existing plaintext values migrated automatically on startup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/api"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/assets"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/crypto"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/metrics"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/monitor"
|
||||
@@ -88,11 +89,20 @@ func main() {
|
||||
discoveredPaths := discoverHDDPaths(cfg.Paths.StacksDir, logger)
|
||||
sett.AutoDiscoverStoragePaths(discoveredPaths, cfg.Paths.HDDPath, logger)
|
||||
|
||||
// --- Load or create encryption key ---
|
||||
encKeyPath := filepath.Join(cfg.Paths.DataDir, "encryption.key")
|
||||
encKey, err := crypto.LoadOrCreateKey(encKeyPath)
|
||||
if err != nil {
|
||||
logger.Fatalf("[FATAL] Failed to load encryption key: %v", err)
|
||||
}
|
||||
logger.Printf("[INFO] Encryption key loaded from %s", encKeyPath)
|
||||
|
||||
// --- Initialize stack manager ---
|
||||
stackMgr, err := stacks.NewManager(cfg, logger)
|
||||
if err != nil {
|
||||
logger.Fatalf("[FATAL] Failed to initialize stack manager: %v", err)
|
||||
}
|
||||
stackMgr.SetEncryptionKey(encKey)
|
||||
|
||||
// Initial stack scan
|
||||
if err := stackMgr.ScanStacks(); err != nil {
|
||||
@@ -104,6 +114,9 @@ func main() {
|
||||
stackMgr.InjectMissingFields(names)
|
||||
}
|
||||
|
||||
// Migrate existing plaintext passwords to encrypted
|
||||
stackMgr.MigrateEncryption()
|
||||
|
||||
// --- Initialize catalog syncer ---
|
||||
syncer := catalogsync.New(cfg, logger, stackMgr.ScanStacks, func(updated []string) {
|
||||
stackMgr.InjectMissingFields(updated)
|
||||
@@ -590,6 +603,7 @@ func main() {
|
||||
}
|
||||
// --- Initialize web server ---
|
||||
webServer := web.NewServer(cfg, stackMgr, cpuCollector, backupMgr, crossDriveRunner, sched, sett, alertMgr, notifier, updater, logger, Version)
|
||||
webServer.SetEncryptionKey(encKey)
|
||||
webServer.SetStorageWatchdog(storageWatchdog)
|
||||
if assetsSyncer != nil {
|
||||
webServer.SetAssetsSyncer(assetsSyncer)
|
||||
@@ -942,7 +956,7 @@ func (a *driveMigrateStackAdapter) UpdateStackHDDPath(name, newPath string) erro
|
||||
return fmt.Errorf("app.yaml not found for stack: %s", name)
|
||||
}
|
||||
appCfg.Env["HDD_PATH"] = newPath
|
||||
return stacks.SaveAppConfig(stackDir, appCfg)
|
||||
return stacks.SaveAppConfig(stackDir, appCfg, nil, nil)
|
||||
}
|
||||
|
||||
func (a *driveMigrateStackAdapter) StackExists(name string) bool {
|
||||
@@ -954,11 +968,13 @@ func (a *driveMigrateStackAdapter) StackExists(name string) bool {
|
||||
func pushInfraBackup(cfg *config.Config, sett *settings.Settings,
|
||||
stackProv *stackAdapter, pusher *report.Pusher, logger *log.Logger) {
|
||||
|
||||
encKeyPath := filepath.Join(cfg.Paths.DataDir, "encryption.key")
|
||||
ib, err := report.BuildInfraBackup(
|
||||
cfg.Customer.ID, cfg.Customer.Domain, Version,
|
||||
"/opt/docker/felhom-controller/controller.yaml",
|
||||
filepath.Join(cfg.Paths.DataDir, "settings.json"),
|
||||
cfg.Backup.ResticPasswordFile,
|
||||
encKeyPath,
|
||||
cfg.Paths.SystemDataPath,
|
||||
sett, stackProv, logger,
|
||||
)
|
||||
@@ -1053,11 +1069,13 @@ func runSetupMode(cfg *config.Config, logger *log.Logger) {
|
||||
func writeLocalInfraBackup(cfg *config.Config, sett *settings.Settings,
|
||||
stackProv *stackAdapter, logger *log.Logger) {
|
||||
|
||||
encKeyPath := filepath.Join(cfg.Paths.DataDir, "encryption.key")
|
||||
ib, err := report.BuildInfraBackup(
|
||||
cfg.Customer.ID, cfg.Customer.Domain, Version,
|
||||
"/opt/docker/felhom-controller/controller.yaml",
|
||||
filepath.Join(cfg.Paths.DataDir, "settings.json"),
|
||||
cfg.Backup.ResticPasswordFile,
|
||||
encKeyPath,
|
||||
cfg.Paths.SystemDataPath,
|
||||
sett, stackProv, logger,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user