Skip to content

Commit 93d82c3

Browse files
committed
feat(auth): secure internal gRPC with JWT
Signed-off-by: CooooolFrog <zuliangwanghust@gmail.com>
1 parent 07bd92a commit 93d82c3

26 files changed

Lines changed: 2319 additions & 22 deletions

File tree

docs/design/grpc-jwt-authentication.md

Lines changed: 500 additions & 0 deletions
Large diffs are not rendered by default.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ require (
2626
github.com/go-gorm/caches/v4 v4.0.5
2727
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a
2828
github.com/go-playground/validator/v10 v10.30.3
29+
github.com/golang-jwt/jwt/v4 v4.5.2
2930
github.com/go-redis/cache/v9 v9.0.0
3031
github.com/go-redis/redis_rate/v10 v10.0.1
3132
github.com/go-redis/redismock/v9 v9.2.0
@@ -126,7 +127,6 @@ require (
126127
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
127128
github.com/goccy/go-json v0.10.6 // indirect
128129
github.com/goccy/go-yaml v1.19.2 // indirect
129-
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
130130
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
131131
github.com/golang-sql/sqlexp v0.1.0 // indirect
132132
github.com/golang/mock v1.6.0 // indirect

manager/config/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"d7y.io/dragonfly/v2/cmd/dependency/base"
2828
"d7y.io/dragonfly/v2/pkg/net/ip"
29+
grpcauth "d7y.io/dragonfly/v2/pkg/rpc/auth/jwt"
2930
"d7y.io/dragonfly/v2/pkg/types"
3031
)
3132

@@ -39,6 +40,9 @@ type Config struct {
3940
// Auth configuration.
4041
Auth AuthConfig `yaml:"auth" mapstructure:"auth"`
4142

43+
// GRPCAuth is the inter-component gRPC authentication configuration.
44+
GRPCAuth grpcauth.Config `yaml:"grpcAuth" mapstructure:"grpcAuth"`
45+
4246
// Database configuration.
4347
Database DatabaseConfig `yaml:"database" mapstructure:"database"`
4448

@@ -441,6 +445,7 @@ func New() *Config {
441445
MaxRefresh: DefaultJWTMaxRefresh,
442446
},
443447
},
448+
GRPCAuth: grpcauth.DefaultConfig(),
444449
Database: DatabaseConfig{
445450
Type: DatabaseTypeMysql,
446451
Mysql: MysqlConfig{
@@ -533,6 +538,10 @@ func (cfg *Config) Validate() error {
533538
return errors.New("grpc requires parameter requestRateLimit")
534539
}
535540

541+
if err := grpcauth.ValidateConfig(cfg.GRPCAuth); err != nil {
542+
return err
543+
}
544+
536545
if cfg.Server.REST.TLS != nil {
537546
if cfg.Server.REST.TLS.Cert == "" {
538547
return errors.New("rest tls requires parameter cert")

manager/config/config_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424

2525
"github.com/stretchr/testify/assert"
2626
"gopkg.in/yaml.v3"
27+
28+
grpcauth "d7y.io/dragonfly/v2/pkg/rpc/auth/jwt"
2729
)
2830

2931
var (
@@ -127,6 +129,22 @@ func TestConfig_Load(t *testing.T) {
127129
MaxRefresh: 1 * time.Minute,
128130
},
129131
},
132+
GRPCAuth: grpcauth.Config{
133+
Mode: grpcauth.ModeRequired,
134+
RequireTransportSecurity: true,
135+
JWT: grpcauth.JWTConfig{
136+
Issuer: "dragonfly-test",
137+
TokenTTL: 10 * time.Minute,
138+
MaxTokenTTL: 15 * time.Minute,
139+
ClockSkew: 30 * time.Second,
140+
RefreshBefore: time.Minute,
141+
ActiveKeyID: "test-key",
142+
Keys: []grpcauth.KeyConfig{{
143+
ID: "test-key",
144+
SecretFile: "/etc/dragonfly/secrets/grpc-jwt/test-key",
145+
}},
146+
},
147+
},
130148
Database: DatabaseConfig{
131149
Type: "mysql",
132150
Mysql: MysqlConfig{
@@ -262,6 +280,16 @@ func TestConfig_Validate(t *testing.T) {
262280
assert.EqualError(err, "server requires parameter name")
263281
},
264282
},
283+
{
284+
name: "grpc auth rejects unsupported mode",
285+
config: New(),
286+
mock: func(cfg *Config) {
287+
cfg.GRPCAuth.Mode = "unknown"
288+
},
289+
expect: func(t *testing.T, err error) {
290+
assert.EqualError(t, err, `grpc auth has unsupported mode "unknown"`)
291+
},
292+
},
265293
{
266294
name: "grpc requires parameter advertiseIP",
267295
config: New(),

manager/config/testdata/manager.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ auth:
3030
timeout: 30s
3131
maxRefresh: 1m
3232

33+
grpcAuth:
34+
mode: required
35+
requireTransportSecurity: true
36+
jwt:
37+
issuer: dragonfly-test
38+
tokenTTL: 10m
39+
maxTokenTTL: 15m
40+
clockSkew: 30s
41+
refreshBefore: 1m
42+
activeKeyID: test-key
43+
keys:
44+
- id: test-key
45+
secretFile: /etc/dragonfly/secrets/grpc-jwt/test-key
46+
3347
database:
3448
type: mysql
3549
mysql:

manager/manager.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import (
4646
pkggc "d7y.io/dragonfly/v2/pkg/gc"
4747
"d7y.io/dragonfly/v2/pkg/redis"
4848
"d7y.io/dragonfly/v2/pkg/rpc"
49+
grpcauth "d7y.io/dragonfly/v2/pkg/rpc/auth/jwt"
4950
)
5051

5152
const (
@@ -113,6 +114,11 @@ type Server struct {
113114
// New creates a new manager server.
114115
func New(cfg *config.Config, d dfpath.Dfpath) (*Server, error) {
115116
s := &Server{config: cfg}
117+
authenticator, err := grpcauth.New(cfg.GRPCAuth)
118+
if err != nil {
119+
return nil, err
120+
}
121+
logger.Infof("initialized gRPC authentication with mode %s", authenticator.Mode())
116122

117123
// Initialize database.
118124
db, err := database.New(cfg)
@@ -189,7 +195,7 @@ func New(cfg *config.Config, d dfpath.Dfpath) (*Server, error) {
189195
}
190196

191197
// Initialize GRPC server.
192-
_, grpcServer, err := rpcserver.New(cfg, db, cache, searcher, options...)
198+
_, grpcServer, err := rpcserver.NewWithAuthentication(cfg, authenticator, db, cache, searcher, options...)
193199
if err != nil {
194200
return nil, err
195201
}

manager/rpcserver/rpcserver.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"d7y.io/dragonfly/v2/manager/database"
2727
"d7y.io/dragonfly/v2/manager/models"
2828
"d7y.io/dragonfly/v2/manager/searcher"
29+
grpcauth "d7y.io/dragonfly/v2/pkg/rpc/auth/jwt"
2930
managerserver "d7y.io/dragonfly/v2/pkg/rpc/manager/server"
3031
)
3132

@@ -51,6 +52,14 @@ type Server struct {
5152
func New(
5253
cfg *config.Config, database *database.Database, cache *cache.Cache, searcher searcher.Searcher,
5354
opts ...grpc.ServerOption) (*Server, *grpc.Server, error) {
55+
return NewWithAuthentication(cfg, nil, database, cache, searcher, opts...)
56+
}
57+
58+
// NewWithAuthentication returns a new manager server with inter-component JWT
59+
// authentication.
60+
func NewWithAuthentication(
61+
cfg *config.Config, authenticator *grpcauth.Authenticator, database *database.Database, cache *cache.Cache, searcher searcher.Searcher,
62+
opts ...grpc.ServerOption) (*Server, *grpc.Server, error) {
5463
s := &Server{
5564
config: cfg,
5665
db: database.DB,
@@ -59,10 +68,11 @@ func New(
5968
searcher: searcher,
6069
}
6170

62-
return s, managerserver.New(
71+
return s, managerserver.NewWithAuthentication(
6372
newManagerServerV1(s.config, database, s.cache, s.searcher),
6473
newManagerServerV2(s.config, database, s.cache, s.searcher),
6574
cfg.Server.GRPC.RequestRateLimit,
75+
authenticator,
6676
opts...), nil
6777
}
6878

0 commit comments

Comments
 (0)