Полный production-фикс fix30/fix30.1: auth, smoke, bootstrap, preflight, rollback и валидация PATCH

This commit is contained in:
2026-05-09 02:46:48 +05:00
parent 1139c428b6
commit 78da898ba5
13 changed files with 177 additions and 20 deletions
+1
View File
@@ -92,6 +92,7 @@ func UpdatePeer(c *gin.Context) {
if err != nil {
return
}
peerUpdateDto.Id = &id
if peerUpdateDto.Name != nil && *peerUpdateDto.Name != "" && service.ExistPeerName(*peerUpdateDto.Name, id) {
vo.Fail(fmt.Sprintf("name %s already exists", *peerUpdateDto.Name), c)
return
+1
View File
@@ -31,6 +31,7 @@ func validateField[T interface{}](c *gin.Context, field T) (T, error) {
bindErr = c.ShouldBindQuery(&field)
} else if c.Request.Method == http.MethodPost ||
c.Request.Method == http.MethodPut ||
c.Request.Method == http.MethodPatch ||
c.Request.Method == http.MethodDelete {
bindErr = c.ShouldBindJSON(&field)
}
+122
View File
@@ -1,6 +1,7 @@
package dao
import (
"encoding/base64"
"errors"
"fmt"
"github.com/glebarez/sqlite"
@@ -63,6 +64,9 @@ func InitSql(port string) error {
if err := ensureSecureBootstrapAdmin(); err != nil {
return err
}
if err := ensureSecureBootstrapPeer(); err != nil {
return err
}
if _, err := getOrCreateJwtSecret(); err != nil {
return err
}
@@ -143,6 +147,124 @@ func ensureSecureBootstrapAdmin() error {
return nil
}
func ensureSecureBootstrapPeer() error {
bootstrapSecret := strings.TrimSpace(os.Getenv("HY2XS_ADMIN_CON_PASS"))
if bootstrapSecret == "" {
generated, err := util.RandomString(24)
if err != nil {
return err
}
bootstrapSecret = generated
}
if _, err := GetPeer("auth_id = ?", "bootstrap-admin-peer"); err == nil {
return nil
}
if _, err := GetPeer("name = ?", "bootstrap-admin-peer"); err == nil {
return nil
}
secretDigest, err := buildPeerSecretDigest(bootstrapSecret)
if err != nil {
return err
}
if _, err := GetPeer("secret_digest = ?", secretDigest); err == nil {
return nil
}
secretEncrypted, err := encryptBootstrapPeerSecret(bootstrapSecret)
if err != nil {
return err
}
name := "bootstrap-admin-peer"
authID := "bootstrap-admin-peer"
remark := "bootstrap peer seeded from HY2XS_ADMIN_CON_PASS"
quota := int64(0)
download := int64(0)
upload := int64(0)
expires := int64(0)
maxDevices := int64(3)
disabled := int64(0)
bannedUntil := int64(0)
lastConnection := int64(0)
peer := entity.Peer{
Name: &name,
Remark: &remark,
AuthId: &authID,
SecretDigest: &secretDigest,
SecretEncrypted: &secretEncrypted,
QuotaBytes: &quota,
DownloadBytes: &download,
UploadBytes: &upload,
ExpiresAt: &expires,
MaxDevices: &maxDevices,
Disabled: &disabled,
BannedUntil: &bannedUntil,
LastConnectionAt: &lastConnection,
}
_, saveErr := SavePeer(peer)
return saveErr
}
func buildPeerSecretDigest(rawSecret string) (string, error) {
secretKey, err := getOrCreatePeerSecretDigestKey()
if err != nil {
return "", err
}
return util.HmacSHA256Hex(rawSecret, secretKey), nil
}
func getOrCreatePeerSecretEncryptionKey() ([]byte, error) {
raw, err := getOrCreateConfigKey(constant.PeerSecretEncryptionKey, 32)
if err != nil {
return nil, err
}
decoded, decErr := util.DecodeBase64Key(raw, 32)
if decErr == nil {
return decoded, nil
}
plain := []byte(strings.TrimSpace(raw))
if len(plain) < 32 {
return nil, errors.New("invalid peer secret encryption key")
}
plain = plain[:32]
encoded := base64.StdEncoding.EncodeToString(plain)
if err := UpdateConfig([]string{constant.PeerSecretEncryptionKey}, map[string]interface{}{"value": encoded}); err != nil {
return nil, err
}
return plain, nil
}
func encryptBootstrapPeerSecret(rawSecret string) (string, error) {
key, err := getOrCreatePeerSecretEncryptionKey()
if err != nil {
return "", err
}
return util.EncryptAESGCM(rawSecret, key)
}
func getOrCreateConfigKey(key string, size int) (string, error) {
cfg, err := GetConfig("key = ?", key)
if err == nil && cfg.Value != nil && strings.TrimSpace(*cfg.Value) != "" {
return strings.TrimSpace(*cfg.Value), nil
}
raw, genErr := util.RandomString(size)
if genErr != nil {
return "", genErr
}
value := raw
remark := key
if _, saveErr := SaveConfig(entity.Config{Key: &key, Value: &value, Remark: &remark}); saveErr != nil {
if updErr := UpdateConfig([]string{key}, map[string]interface{}{"value": value}); updErr != nil {
return "", updErr
}
}
return value, nil
}
func runMigrations() error {
if tx := sqliteDB.Exec(`CREATE TABLE IF NOT EXISTS schema_migrations (
version TEXT PRIMARY KEY,
-4
View File
@@ -31,10 +31,6 @@ func resolvePublicEndpoint() (string, int, error) {
}
func Hysteria2Auth(conPass string) (int64, string, error) {
if !Hysteria2IsRunning() {
return 0, "", errors.New("hysteria2 is not running")
}
now := time.Now().UnixMilli()
secretDigest, digestErr := PeerSecretDigest(conPass)
if digestErr != nil {