fix(database): replace the stored session secret once on upgrade

The session cookie key is generated once and kept forever. Keys stored by
older releases came from a weaker generator, and a stored value cannot be
told apart from a current one, so drop it once through a seeder and let
GetSecret store a fresh crypto/rand key on the next start. Admins log in
again once after upgrading; fresh installs record the seeder up front.
This commit is contained in:
MHSanaei
2026-10-31 08:10:36 +00:00
parent c621ba9014
commit 4167597a15
2 changed files with 68 additions and 1 deletions
+18 -1
View File
@@ -1527,7 +1527,7 @@ func runSeeders(isUsersEmpty bool) error {
}
if empty && isUsersEmpty {
seeders := []string{"UserPasswordHash", "ClientsTable", "InboundClientsArrayFix", "InboundClientTgIdFix2", "InboundClientSubIdFix", "FreedomFinalRulesReverseFix", "FreedomFinalRulesPrivateEgressBlock", "UppercaseFreedomFinalRulesFix", "InboundRealityFinalmaskTcpStrip", "ApiTokensHash", "LegacyProxySettingsCleanup", "OutboundRemovedKeysFix", "FreedomDomainStrategyFix", "DNSOutboundLegacyKeysFix", "DNSOutboundQTypeZeroFix", "WireguardDomainStrategyFix", "XdnsFinalmaskNamesAddrsFix", "UDPHopClientMaskFix", "HostVerifyPeerCertByNameBoolFix", "HostMihomoX25519Reset", "WireguardPeersToClients", "MtprotoSecretsToClients", "NodeInboundsAdopted", "NodeDownNotified", "ResetIpLimitNoFail2ban"}
seeders := []string{"UserPasswordHash", "ClientsTable", "InboundClientsArrayFix", "InboundClientTgIdFix2", "InboundClientSubIdFix", "FreedomFinalRulesReverseFix", "FreedomFinalRulesPrivateEgressBlock", "UppercaseFreedomFinalRulesFix", "InboundRealityFinalmaskTcpStrip", "ApiTokensHash", "LegacyProxySettingsCleanup", "SessionSecretRotation", "OutboundRemovedKeysFix", "FreedomDomainStrategyFix", "DNSOutboundLegacyKeysFix", "DNSOutboundQTypeZeroFix", "WireguardDomainStrategyFix", "XdnsFinalmaskNamesAddrsFix", "UDPHopClientMaskFix", "HostVerifyPeerCertByNameBoolFix", "HostMihomoX25519Reset", "WireguardPeersToClients", "MtprotoSecretsToClients", "NodeInboundsAdopted", "NodeDownNotified", "ResetIpLimitNoFail2ban"}
for _, name := range seeders {
if err := db.Create(&model.HistoryOfSeeders{SeederName: name}).Error; err != nil {
return err
@@ -1638,6 +1638,12 @@ func runSeeders(isUsersEmpty bool) error {
}
}
if !slices.Contains(seedersHistory, "SessionSecretRotation") {
if err := dropPreUpgradeSessionSecret(); err != nil {
return err
}
}
if !slices.Contains(seedersHistory, "OutboundRemovedKeysFix") {
if err := migrateOutboundRemovedKeys(); err != nil {
return err
@@ -1899,6 +1905,17 @@ func clearLegacyProxySettings() error {
})
}
// Secrets stored before v2.8.3 came from math/rand (a 31-bit clock seed up to v2.1.3);
// GetSecret draws a crypto/rand one on the next start.
func dropPreUpgradeSessionSecret() error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("key = ?", "secret").Delete(&model.Setting{}).Error; err != nil {
return err
}
return tx.Create(&model.HistoryOfSeeders{SeederName: "SessionSecretRotation"}).Error
})
}
func migrateOutboundRemovedKeys() error {
var setting model.Setting
err := db.Model(model.Setting{}).Where("key = ?", "xrayTemplateConfig").First(&setting).Error