package middleware import ( "crypto/subtle" "net/http" "strings" "github.com/gin-gonic/gin" "hy2xs-admin/dao" "hy2xs-admin/model/constant" ) func MachineAuthHandler() gin.HandlerFunc { return func(c *gin.Context) { secretCfg, err := dao.GetConfig("key = ?", constant.Hysteria2TrafficStatsSecret) if err != nil || secretCfg.Value == nil { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ "code": http.StatusForbidden, "type": "no", "message": "machine auth failed", "data": nil, }) return } expected := strings.TrimSpace(*secretCfg.Value) if expected == "" { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ "code": http.StatusForbidden, "type": "no", "message": "machine auth not configured", "data": nil, }) return } provided := strings.TrimSpace(c.Query("access_token")) if provided == "" { provided = strings.TrimSpace(c.GetHeader("X-HY2XS-Machine-Token")) } // Сравнение постоянного времени: обычное `!=` завершается на первом // несовпавшем байте. Endpoint слушает только loopback, но локальный // непривилегированный процесс — ровно та позиция, из которой такой // замер и делается, а ценой ошибки здесь является machine token. if provided == "" || subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) != 1 { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ "code": http.StatusForbidden, "type": "no", "message": "machine auth failed", "data": nil, }) return } c.Next() } }