VYPR
Critical severityNVD Advisory· Published Sep 19, 2024· Updated Sep 26, 2024

Dragonfly2 vulnerable to hard coded cyptographic key

CVE-2023-27584

Description

Dragonfly is an open source P2P-based file distribution and image acceleration system. It is hosted by the Cloud Native Computing Foundation (CNCF) as an Incubating Level Project. Dragonfly uses JWT to verify user. However, the secret key for JWT, "Secret Key", is hard coded, which leads to authentication bypass. An attacker can perform any action as a user with admin privileges. This issue has been addressed in release version 2.0.9. All users are advised to upgrade. There are no known workarounds for this vulnerability.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Hard-coded JWT secret key in Dragonfly before v2.0.9 allows attackers to forge admin tokens and achieve authentication bypass.

Vulnerability

Overview

CVE-2023-27584: The Dragonfly P2P-based file distribution system uses a hard-coded JWT secret key (key: "ZHJhZ29uZmx5Cg==", which is the base64 encoding of "dragonfly") [2][3]. This static secret enables an attacker to forge valid JWT tokens for any user, including those with administrative privileges, bypassing authentication entirely [1].

Attack

Vector and Exploitation

The vulnerability resides in the JWT authentication mechanism used to verify user identities. Since the secret key is hard-coded in the default configuration and was not configurable prior to version 2.0.9 [2][3], an unauthenticated attacker can craft their own JWT tokens without needing to know any user credentials. The attack requires no special network position or prior access—anyone able to reach the Dragonfly manager or scheduler API can generate an admin token and use it to issue arbitrary API requests [1].

Impact

Successful exploitation grants an attacker full administrative control over the Dragonfly system [1]. This includes the ability to create, modify, or delete users, modify system configurations, access or manipulate P2P distribution settings, and potentially compromise the integrity and availability of the file distribution and image acceleration services. Because the default secret is the same across all unpatched instances, an attacker can target any Dragonfly deployment running version 2.0.8 or earlier [1].

Mitigation

The Dragonfly project addressed this issue in release v2.0.9 by introducing an auth configuration section that allows administrators to set a custom JWT secret key [2][3][4]. All users should upgrade to version 2.0.9 or later and configure a unique, strong secret key in production. There is no known workaround [1].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
d7y.io/dragonfly/v2Go
>= 2.1.0-alpha.0, < 2.1.0-beta.12.1.0-beta.1
d7y.io/dragonfly/v2Go
< 2.0.9-rc.22.0.9-rc.2

Affected products

1

Patches

2
684469a31bd2

feat: add auth config to manager (#2161)

9 files changed · +190 12
  • deploy/docker-compose/template/manager.template.yaml+15 0 modified
    @@ -32,6 +32,21 @@ server:
       # In macos(just for testing), default value is /Users/$USER/.dragonfly/plugins.
       pluginDir: ''
     
    +auth:
    +  jwt:
    +    # Realm name to display to the user, default value is Dragonfly.
    +    realm: "Dragonfly"
    +    # Key is secret key used for signing, default value is
    +    # encoded base64 of dragonfly.
    +    # Please change the key in production.
    +    key: "ZHJhZ29uZmx5Cg=="
    +    # Timeout is duration that a jwt token is valid,
    +    # default duration is two days.
    +    timeout: 48h
    +    # MaxRefresh field allows clients to refresh their token
    +    # until MaxRefresh has passed, default duration is two days.
    +    maxRefresh: 48h
    +
     # Database info used for server.
     database:
       # Database type, supported types include mysql, mariadb and postgres.
    
  • deploy/helm-charts+1 1 modified
    @@ -1 +1 @@
    -Subproject commit be86afe69937a54f9f94c75be986a74ce4dbc2c0
    +Subproject commit 1608dd49248269497c44e0112e48540be9f04a9e
    
  • manager/config/config.go+45 0 modified
    @@ -37,6 +37,9 @@ type Config struct {
     	// Server configuration.
     	Server ServerConfig `yaml:"server" mapstructure:"server"`
     
    +	// Auth configuration.
    +	Auth AuthConfig `yaml:"auth" mapstructure:"auth"`
    +
     	// Database configuration.
     	Database DatabaseConfig `yaml:"database" mapstructure:"database"`
     
    @@ -79,6 +82,25 @@ type ServerConfig struct {
     	REST RestConfig `yaml:"rest" mapstructure:"rest"`
     }
     
    +type AuthConfig struct {
    +	// JWT configuration.
    +	JWT JWTConfig `yaml:"jwt" mapstructure:"jwt"`
    +}
    +
    +type JWTConfig struct {
    +	// Realm name to display to the user, default value is Dragonfly.
    +	Realm string `yaml:"realm" mapstructure:"realm"`
    +
    +	// Key is secret key used for signing. Please change the key in production
    +	Key string `yaml:"key" mapstructure:"key"`
    +
    +	// Timeout is duration that a jwt token is valid, default duration is two days.
    +	Timeout time.Duration `yaml:"timeout" mapstructure:"timeout"`
    +
    +	// MaxRefresh field allows clients to refresh their token until MaxRefresh has passed, default duration is two days.
    +	MaxRefresh time.Duration `yaml:"maxRefresh" mapstructure:"maxRefresh"`
    +}
    +
     type DatabaseConfig struct {
     	// Database type.
     	Type string `yaml:"type" mapstructure:"type"`
    @@ -324,6 +346,13 @@ func New() *Config {
     				Addr: DefaultRESTAddr,
     			},
     		},
    +		Auth: AuthConfig{
    +			JWT: JWTConfig{
    +				Realm:      DefaultJWTRealm,
    +				Timeout:    DefaultJWTTimeout,
    +				MaxRefresh: DefaultJWTMaxRefresh,
    +			},
    +		},
     		Database: DatabaseConfig{
     			Type: DatabaseTypeMysql,
     			Mysql: MysqlConfig{
    @@ -391,6 +420,22 @@ func (cfg *Config) Validate() error {
     		return errors.New("grpc requires parameter listenIP")
     	}
     
    +	if cfg.Auth.JWT.Realm == "" {
    +		return errors.New("jwt requires parameter realm")
    +	}
    +
    +	if cfg.Auth.JWT.Key == "" {
    +		return errors.New("jwt requires parameter key")
    +	}
    +
    +	if cfg.Auth.JWT.Timeout == 0 {
    +		return errors.New("jwt requires parameter timeout")
    +	}
    +
    +	if cfg.Auth.JWT.MaxRefresh == 0 {
    +		return errors.New("jwt requires parameter maxRefresh")
    +	}
    +
     	if cfg.Database.Type == "" {
     		return errors.New("database requires parameter type")
     	}
    
  • manager/config/config_test.go+96 0 modified
    @@ -31,6 +31,13 @@ import (
     )
     
     var (
    +	mockJWTConfig = JWTConfig{
    +		Realm:      "foo",
    +		Key:        "bar",
    +		Timeout:    30 * time.Second,
    +		MaxRefresh: 1 * time.Minute,
    +	}
    +
     	mockMysqlConfig = MysqlConfig{
     		User:      "foo",
     		Password:  "bar",
    @@ -118,6 +125,14 @@ func TestConfig_Load(t *testing.T) {
     				Addr: ":8080",
     			},
     		},
    +		Auth: AuthConfig{
    +			JWT: JWTConfig{
    +				Realm:      "foo",
    +				Key:        "bar",
    +				Timeout:    30 * time.Second,
    +				MaxRefresh: 1 * time.Minute,
    +			},
    +		},
     		Database: DatabaseConfig{
     			Type: "mysql",
     			Mysql: MysqlConfig{
    @@ -216,6 +231,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "valid config",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
     			},
    @@ -257,10 +273,57 @@ func TestConfig_Validate(t *testing.T) {
     				assert.EqualError(err, "grpc requires parameter listenIP")
     			},
     		},
    +		{
    +			name:   "jwt requires parameter realm",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT.Realm = ""
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter realm")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter key",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT.Key = ""
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter key")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter timeout",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
    +				cfg.Auth.JWT.Timeout = 0
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter timeout")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter maxRefresh",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
    +				cfg.Auth.JWT.MaxRefresh = 0
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter maxRefresh")
    +			},
    +		},
     		{
     			name:   "database requires parameter type",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = ""
     			},
     			expect: func(t *testing.T, err error) {
    @@ -272,6 +335,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter user",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.User = ""
    @@ -285,6 +349,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter password",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Password = ""
    @@ -298,6 +363,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter host",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Host = ""
    @@ -311,6 +377,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter port",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Port = 0
    @@ -324,6 +391,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter dbname",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.DBName = ""
    @@ -337,6 +405,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter cert",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -351,6 +420,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter key",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -366,6 +436,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter ca",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -382,6 +453,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter user",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.User = ""
    @@ -395,6 +467,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter password",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Password = ""
    @@ -408,6 +481,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter host",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Host = ""
    @@ -421,6 +495,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter port",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Port = 0
    @@ -434,6 +509,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter dbname",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.DBName = ""
    @@ -447,6 +523,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter sslMode",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.SSLMode = ""
    @@ -460,6 +537,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter timezone",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Timezone = ""
    @@ -473,6 +551,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter addrs",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -487,6 +566,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter db",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -501,6 +581,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter brokerDB",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -515,6 +596,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter backendDB",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -529,6 +611,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter ttl",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -543,6 +626,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "local requires parameter size",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -557,6 +641,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "local requires parameter ttl",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -571,6 +656,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter name",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -586,6 +672,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "invalid objectStorage name",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -601,6 +688,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter accessKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -616,6 +704,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter secretKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -631,6 +720,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "metrics requires parameter addr",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -646,6 +736,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter caCert",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -661,6 +752,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter caKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -676,6 +768,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter tlsPolicy",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -691,6 +784,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter ipAddresses",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -706,6 +800,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter dnsNames",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -721,6 +816,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter validityPeriod",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    
  • manager/config/constants.go+11 0 modified
    @@ -45,6 +45,17 @@ const (
     	DefaultRESTAddr = ":8080"
     )
     
    +const (
    +	// DefaultJWTTimeout is default of name in jwt.
    +	DefaultJWTRealm = "Dragonfly"
    +
    +	// DefaultJWTTimeout is default of timeout in jwt.
    +	DefaultJWTTimeout = 2 * 24 * time.Hour
    +
    +	// DefaultJWTMaxRefresh is default of max refresh in jwt.
    +	DefaultJWTMaxRefresh = 2 * 24 * time.Hour
    +)
    +
     const (
     	// DefaultRedisDB is default db for redis.
     	DefaultRedisDB = 0
    
  • manager/config/testdata/manager.yaml+7 0 modified
    @@ -13,6 +13,13 @@ server:
       rest:
         addr: :8080
     
    +auth:
    +  jwt:
    +    realm: foo
    +    key: bar
    +    timeout: 30s
    +    maxRefresh: 1m
    +
     database:
       type: mysql
       mysql:
    
  • manager/middlewares/jwt.go+13 9 modified
    @@ -24,25 +24,29 @@ import (
     	jwt "github.com/appleboy/gin-jwt/v2"
     	"github.com/gin-gonic/gin"
     
    +	"d7y.io/dragonfly/v2/manager/config"
     	"d7y.io/dragonfly/v2/manager/model"
     	"d7y.io/dragonfly/v2/manager/service"
     	"d7y.io/dragonfly/v2/manager/types"
     )
     
    -func Jwt(service service.Service) (*jwt.GinJWTMiddleware, error) {
    -	identityKey := "id"
    +const (
    +	// defaultIdentityKey is default of the identity key.
    +	defaultIdentityKey = "id"
    +)
     
    +func Jwt(cfg config.JWTConfig, service service.Service) (*jwt.GinJWTMiddleware, error) {
     	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
    -		Realm:       "Dragonfly",
    -		Key:         []byte("Secret Key"),
    -		Timeout:     2 * 24 * time.Hour,
    -		MaxRefresh:  2 * 24 * time.Hour,
    -		IdentityKey: identityKey,
    +		Realm:       cfg.Realm,
    +		Key:         []byte(cfg.Key),
    +		Timeout:     cfg.Timeout,
    +		MaxRefresh:  cfg.MaxRefresh,
    +		IdentityKey: defaultIdentityKey,
     
     		IdentityHandler: func(c *gin.Context) any {
     			claims := jwt.ExtractClaims(c)
     
    -			id, ok := claims[identityKey]
    +			id, ok := claims[defaultIdentityKey]
     			if !ok {
     				c.JSON(http.StatusUnauthorized, gin.H{
     					"message": "Unavailable token: require user id",
    @@ -82,7 +86,7 @@ func Jwt(service service.Service) (*jwt.GinJWTMiddleware, error) {
     		PayloadFunc: func(data any) jwt.MapClaims {
     			if user, ok := data.(*model.User); ok {
     				return jwt.MapClaims{
    -					identityKey: user.ID,
    +					defaultIdentityKey: user.ID,
     				}
     			}
     
    
  • manager/router/router.go+1 1 modified
    @@ -78,7 +78,7 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
     	r.Use(cors.New(corsConfig))
     
     	rbac := middlewares.RBAC(enforcer)
    -	jwt, err := middlewares.Jwt(service)
    +	jwt, err := middlewares.Jwt(cfg.Auth.JWT, service)
     	if err != nil {
     		return nil, err
     	}
    
  • test/testdata/charts/config-nydus.yaml+1 1 modified
    @@ -12,7 +12,7 @@ resources:
     dragonfly:
       enable: true
       mirrorConfig:
    -    host: "http://127.0.0.1:65001"
    +  - host: "http://127.0.0.1:65001"
         auth_through: false
         headers:
           "X-Dragonfly-Registry": "https://ghcr.io"
    
e9da69dc4048

feat: add auth config to manager (#2161)

9 files changed · +190 12
  • deploy/docker-compose/template/manager.template.yaml+15 0 modified
    @@ -32,6 +32,21 @@ server:
       # In macos(just for testing), default value is /Users/$USER/.dragonfly/plugins.
       pluginDir: ''
     
    +auth:
    +  jwt:
    +    # Realm name to display to the user, default value is Dragonfly.
    +    realm: "Dragonfly"
    +    # Key is secret key used for signing, default value is
    +    # encoded base64 of dragonfly.
    +    # Please change the key in production.
    +    key: "ZHJhZ29uZmx5Cg=="
    +    # Timeout is duration that a jwt token is valid,
    +    # default duration is two days.
    +    timeout: 48h
    +    # MaxRefresh field allows clients to refresh their token
    +    # until MaxRefresh has passed, default duration is two days.
    +    maxRefresh: 48h
    +
     # Database info used for server.
     database:
       # Database type, supported types include mysql, mariadb and postgres.
    
  • deploy/helm-charts+1 1 modified
    @@ -1 +1 @@
    -Subproject commit be86afe69937a54f9f94c75be986a74ce4dbc2c0
    +Subproject commit 1608dd49248269497c44e0112e48540be9f04a9e
    
  • manager/config/config.go+45 0 modified
    @@ -37,6 +37,9 @@ type Config struct {
     	// Server configuration.
     	Server ServerConfig `yaml:"server" mapstructure:"server"`
     
    +	// Auth configuration.
    +	Auth AuthConfig `yaml:"auth" mapstructure:"auth"`
    +
     	// Database configuration.
     	Database DatabaseConfig `yaml:"database" mapstructure:"database"`
     
    @@ -79,6 +82,25 @@ type ServerConfig struct {
     	REST RestConfig `yaml:"rest" mapstructure:"rest"`
     }
     
    +type AuthConfig struct {
    +	// JWT configuration.
    +	JWT JWTConfig `yaml:"jwt" mapstructure:"jwt"`
    +}
    +
    +type JWTConfig struct {
    +	// Realm name to display to the user, default value is Dragonfly.
    +	Realm string `yaml:"realm" mapstructure:"realm"`
    +
    +	// Key is secret key used for signing. Please change the key in production
    +	Key string `yaml:"key" mapstructure:"key"`
    +
    +	// Timeout is duration that a jwt token is valid, default duration is two days.
    +	Timeout time.Duration `yaml:"timeout" mapstructure:"timeout"`
    +
    +	// MaxRefresh field allows clients to refresh their token until MaxRefresh has passed, default duration is two days.
    +	MaxRefresh time.Duration `yaml:"maxRefresh" mapstructure:"maxRefresh"`
    +}
    +
     type DatabaseConfig struct {
     	// Database type.
     	Type string `yaml:"type" mapstructure:"type"`
    @@ -324,6 +346,13 @@ func New() *Config {
     				Addr: DefaultRESTAddr,
     			},
     		},
    +		Auth: AuthConfig{
    +			JWT: JWTConfig{
    +				Realm:      DefaultJWTRealm,
    +				Timeout:    DefaultJWTTimeout,
    +				MaxRefresh: DefaultJWTMaxRefresh,
    +			},
    +		},
     		Database: DatabaseConfig{
     			Type: DatabaseTypeMysql,
     			Mysql: MysqlConfig{
    @@ -391,6 +420,22 @@ func (cfg *Config) Validate() error {
     		return errors.New("grpc requires parameter listenIP")
     	}
     
    +	if cfg.Auth.JWT.Realm == "" {
    +		return errors.New("jwt requires parameter realm")
    +	}
    +
    +	if cfg.Auth.JWT.Key == "" {
    +		return errors.New("jwt requires parameter key")
    +	}
    +
    +	if cfg.Auth.JWT.Timeout == 0 {
    +		return errors.New("jwt requires parameter timeout")
    +	}
    +
    +	if cfg.Auth.JWT.MaxRefresh == 0 {
    +		return errors.New("jwt requires parameter maxRefresh")
    +	}
    +
     	if cfg.Database.Type == "" {
     		return errors.New("database requires parameter type")
     	}
    
  • manager/config/config_test.go+96 0 modified
    @@ -31,6 +31,13 @@ import (
     )
     
     var (
    +	mockJWTConfig = JWTConfig{
    +		Realm:      "foo",
    +		Key:        "bar",
    +		Timeout:    30 * time.Second,
    +		MaxRefresh: 1 * time.Minute,
    +	}
    +
     	mockMysqlConfig = MysqlConfig{
     		User:      "foo",
     		Password:  "bar",
    @@ -118,6 +125,14 @@ func TestConfig_Load(t *testing.T) {
     				Addr: ":8080",
     			},
     		},
    +		Auth: AuthConfig{
    +			JWT: JWTConfig{
    +				Realm:      "foo",
    +				Key:        "bar",
    +				Timeout:    30 * time.Second,
    +				MaxRefresh: 1 * time.Minute,
    +			},
    +		},
     		Database: DatabaseConfig{
     			Type: "mysql",
     			Mysql: MysqlConfig{
    @@ -216,6 +231,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "valid config",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
     			},
    @@ -257,10 +273,57 @@ func TestConfig_Validate(t *testing.T) {
     				assert.EqualError(err, "grpc requires parameter listenIP")
     			},
     		},
    +		{
    +			name:   "jwt requires parameter realm",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT.Realm = ""
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter realm")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter key",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT.Key = ""
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter key")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter timeout",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
    +				cfg.Auth.JWT.Timeout = 0
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter timeout")
    +			},
    +		},
    +		{
    +			name:   "jwt requires parameter maxRefresh",
    +			config: New(),
    +			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
    +				cfg.Auth.JWT.MaxRefresh = 0
    +			},
    +			expect: func(t *testing.T, err error) {
    +				assert := assert.New(t)
    +				assert.EqualError(err, "jwt requires parameter maxRefresh")
    +			},
    +		},
     		{
     			name:   "database requires parameter type",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = ""
     			},
     			expect: func(t *testing.T, err error) {
    @@ -272,6 +335,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter user",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.User = ""
    @@ -285,6 +349,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter password",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Password = ""
    @@ -298,6 +363,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter host",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Host = ""
    @@ -311,6 +377,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter port",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.Port = 0
    @@ -324,6 +391,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "mysql requires parameter dbname",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.DBName = ""
    @@ -337,6 +405,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter cert",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -351,6 +420,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter key",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -366,6 +436,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "tls requires parameter ca",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Mysql.TLS = mockMysqlTLSConfig
    @@ -382,6 +453,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter user",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.User = ""
    @@ -395,6 +467,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter password",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Password = ""
    @@ -408,6 +481,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter host",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Host = ""
    @@ -421,6 +495,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter port",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Port = 0
    @@ -434,6 +509,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter dbname",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.DBName = ""
    @@ -447,6 +523,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter sslMode",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.SSLMode = ""
    @@ -460,6 +537,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "postgres requires parameter timezone",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypePostgres
     				cfg.Database.Postgres = mockPostgresConfig
     				cfg.Database.Postgres.Timezone = ""
    @@ -473,6 +551,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter addrs",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -487,6 +566,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter db",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -501,6 +581,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter brokerDB",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -515,6 +596,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter backendDB",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -529,6 +611,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "redis requires parameter ttl",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -543,6 +626,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "local requires parameter size",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -557,6 +641,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "local requires parameter ttl",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -571,6 +656,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter name",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -586,6 +672,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "invalid objectStorage name",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -601,6 +688,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter accessKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -616,6 +704,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "objectStorage requires parameter secretKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -631,6 +720,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "metrics requires parameter addr",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -646,6 +736,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter caCert",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -661,6 +752,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter caKey",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -676,6 +768,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "security requires parameter tlsPolicy",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -691,6 +784,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter ipAddresses",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -706,6 +800,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter dnsNames",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    @@ -721,6 +816,7 @@ func TestConfig_Validate(t *testing.T) {
     			name:   "certSpec requires parameter validityPeriod",
     			config: New(),
     			mock: func(cfg *Config) {
    +				cfg.Auth.JWT = mockJWTConfig
     				cfg.Database.Type = DatabaseTypeMysql
     				cfg.Database.Mysql = mockMysqlConfig
     				cfg.Database.Redis = mockRedisConfig
    
  • manager/config/constants.go+11 0 modified
    @@ -45,6 +45,17 @@ const (
     	DefaultRESTAddr = ":8080"
     )
     
    +const (
    +	// DefaultJWTTimeout is default of name in jwt.
    +	DefaultJWTRealm = "Dragonfly"
    +
    +	// DefaultJWTTimeout is default of timeout in jwt.
    +	DefaultJWTTimeout = 2 * 24 * time.Hour
    +
    +	// DefaultJWTMaxRefresh is default of max refresh in jwt.
    +	DefaultJWTMaxRefresh = 2 * 24 * time.Hour
    +)
    +
     const (
     	// DefaultRedisDB is default db for redis.
     	DefaultRedisDB = 0
    
  • manager/config/testdata/manager.yaml+7 0 modified
    @@ -13,6 +13,13 @@ server:
       rest:
         addr: :8080
     
    +auth:
    +  jwt:
    +    realm: foo
    +    key: bar
    +    timeout: 30s
    +    maxRefresh: 1m
    +
     database:
       type: mysql
       mysql:
    
  • manager/middlewares/jwt.go+13 9 modified
    @@ -24,25 +24,29 @@ import (
     	jwt "github.com/appleboy/gin-jwt/v2"
     	"github.com/gin-gonic/gin"
     
    +	"d7y.io/dragonfly/v2/manager/config"
     	"d7y.io/dragonfly/v2/manager/model"
     	"d7y.io/dragonfly/v2/manager/service"
     	"d7y.io/dragonfly/v2/manager/types"
     )
     
    -func Jwt(service service.Service) (*jwt.GinJWTMiddleware, error) {
    -	identityKey := "id"
    +const (
    +	// defaultIdentityKey is default of the identity key.
    +	defaultIdentityKey = "id"
    +)
     
    +func Jwt(cfg config.JWTConfig, service service.Service) (*jwt.GinJWTMiddleware, error) {
     	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
    -		Realm:       "Dragonfly",
    -		Key:         []byte("Secret Key"),
    -		Timeout:     2 * 24 * time.Hour,
    -		MaxRefresh:  2 * 24 * time.Hour,
    -		IdentityKey: identityKey,
    +		Realm:       cfg.Realm,
    +		Key:         []byte(cfg.Key),
    +		Timeout:     cfg.Timeout,
    +		MaxRefresh:  cfg.MaxRefresh,
    +		IdentityKey: defaultIdentityKey,
     
     		IdentityHandler: func(c *gin.Context) any {
     			claims := jwt.ExtractClaims(c)
     
    -			id, ok := claims[identityKey]
    +			id, ok := claims[defaultIdentityKey]
     			if !ok {
     				c.JSON(http.StatusUnauthorized, gin.H{
     					"message": "Unavailable token: require user id",
    @@ -82,7 +86,7 @@ func Jwt(service service.Service) (*jwt.GinJWTMiddleware, error) {
     		PayloadFunc: func(data any) jwt.MapClaims {
     			if user, ok := data.(*model.User); ok {
     				return jwt.MapClaims{
    -					identityKey: user.ID,
    +					defaultIdentityKey: user.ID,
     				}
     			}
     
    
  • manager/router/router.go+1 1 modified
    @@ -78,7 +78,7 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
     	r.Use(cors.New(corsConfig))
     
     	rbac := middlewares.RBAC(enforcer)
    -	jwt, err := middlewares.Jwt(service)
    +	jwt, err := middlewares.Jwt(cfg.Auth.JWT, service)
     	if err != nil {
     		return nil, err
     	}
    
  • test/testdata/charts/config-nydus.yaml+1 1 modified
    @@ -12,7 +12,7 @@ resources:
     dragonfly:
       enable: true
       mirrorConfig:
    -    host: "http://127.0.0.1:65001"
    +  - host: "http://127.0.0.1:65001"
         auth_through: false
         headers:
           "X-Dragonfly-Registry": "https://ghcr.io"
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.