@@ -17,6 +17,7 @@ package server
1717
1818import (
1919 "context"
20+ "crypto/ecdsa"
2021 "crypto/tls"
2122 "fmt"
2223 "os"
@@ -52,22 +53,16 @@ import (
5253
5354// NewGRPCServer new a gRPC server.
5455func NewGRPCServer (c * conf.Server , authConf * conf.Auth , byteService * service.ByteStreamService , rSvc * service.ResourceService , providers backend.Providers , validator protovalidate.Validator , logger log.Logger ) (* grpc.Server , error ) {
55- log := log .NewHelper (logger )
56- // Load the key on initialization instead of on every request
56+ // Parse the public key once on initialization instead of on every request
5757 // TODO: implement jwks endpoint
58- publicKeyPath := authConf .GetPublicKeyPath ()
59- if publicKeyPath == "" {
60- // Maintain backwards compatibility
61- publicKeyPath = authConf .RobotAccountPublicKeyPath
62- }
63-
64- log .Debugw ("msg" , "loading public key from file" , "file" , publicKeyPath )
65-
66- rawKey , err := os .ReadFile (publicKeyPath )
58+ publicKey , err := parsePublicKey (authConf , logger )
6759 if err != nil {
68- return nil , fmt . Errorf ( "failed to load public key: %w" , err )
60+ return nil , err
6961 }
7062
63+ // Share a single keyfunc closure over the parsed key across all interceptors
64+ keyFunc := loadPublicKey (publicKey )
65+
7166 var opts = []grpc.ServerOption {
7267 // Kratos middleware are in practice unary interceptors
7368 grpc .Middleware (
@@ -83,7 +78,7 @@ func NewGRPCServer(c *conf.Server, authConf *conf.Auth, byteService *service.Byt
8378 // If we require a logged in user we
8479 selector .Server (
8580 jwtMiddleware .Server (
86- loadPublicKey ( rawKey ) ,
81+ keyFunc ,
8782 jwtMiddleware .WithSigningMethod (casJWT .SigningMethod ),
8883 jwtMiddleware .WithClaims (func () jwt.Claims { return & casJWT.Claims {} })),
8984 ).Match (requireAuthentication ()).Build (),
@@ -92,7 +87,7 @@ func NewGRPCServer(c *conf.Server, authConf *conf.Auth, byteService *service.Byt
9287 // Streaming interceptors
9388 grpc .StreamInterceptor (
9489 grpcselector .StreamServerInterceptor (
95- grpc_auth .StreamServerInterceptor (jwtAuthFunc (loadPublicKey ( rawKey ) , casJWT .SigningMethod )),
90+ grpc_auth .StreamServerInterceptor (jwtAuthFunc (keyFunc , casJWT .SigningMethod )),
9691 grpcselector .MatchFunc (allButReflectionAPI ),
9792 ),
9893 // grpc prometheus metrics
@@ -163,10 +158,38 @@ func allButReflectionAPI(_ context.Context, callMeta interceptors.CallMeta) bool
163158 return ! reflectionServiceRegexp .MatchString (callMeta .Service )
164159}
165160
166- // load key for verification
167- func loadPublicKey (rawKey []byte ) jwt.Keyfunc {
168- return func (token * jwt.Token ) (interface {}, error ) {
169- return jwt .ParseECPublicKeyFromPEM (rawKey )
161+ // parsePublicKey resolves the configured public key path, reads the file and parses
162+ // the EC public key once. A malformed key therefore fails at server construction
163+ // instead of surfacing as a per-request authentication error.
164+ func parsePublicKey (authConf * conf.Auth , logger log.Logger ) (* ecdsa.PublicKey , error ) {
165+ l := log .NewHelper (logger )
166+
167+ publicKeyPath := authConf .GetPublicKeyPath ()
168+ if publicKeyPath == "" {
169+ // Maintain backwards compatibility with the deprecated field.
170+ publicKeyPath = authConf .RobotAccountPublicKeyPath //nolint:staticcheck // intentional fallback to the deprecated field
171+ }
172+
173+ l .Debugw ("msg" , "loading public key from file" , "file" , publicKeyPath )
174+
175+ rawKey , err := os .ReadFile (publicKeyPath )
176+ if err != nil {
177+ return nil , fmt .Errorf ("failed to load public key: %w" , err )
178+ }
179+
180+ publicKey , err := jwt .ParseECPublicKeyFromPEM (rawKey )
181+ if err != nil {
182+ return nil , fmt .Errorf ("failed to parse public key: %w" , err )
183+ }
184+
185+ return publicKey , nil
186+ }
187+
188+ // loadPublicKey returns a jwt.Keyfunc that hands back the pre-parsed public key,
189+ // avoiding a PEM re-parse on every request.
190+ func loadPublicKey (publicKey * ecdsa.PublicKey ) jwt.Keyfunc {
191+ return func (_ * jwt.Token ) (interface {}, error ) {
192+ return publicKey , nil
170193 }
171194}
172195
0 commit comments