Skip to content

Commit

Permalink
feat: JWKS support
Browse files Browse the repository at this point in the history
  • Loading branch information
d-led committed Dec 19, 2023
1 parent 306ead3 commit e1c3f37
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .link-checker-service.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ privKeyFile = "./dummy.priv.cer"
pubKeyFile = "./public.cer"
signingAlgorithm = "RS384"

# alternatively, via JWKS
# jwksUrl = "http://my-auth-provider/jwks"

# searchForBodyPatterns allows searching for patterns in response bodies
# enabling searchForBodyPatterns will impact checker performance
# this feature is only configurable via the config file
Expand Down
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Notable changes will be documented here

## 0.9.35

- token validation via JWKS

## 0.9.34

- upgraded dependencies

## 0.9.33

- Go v1.21
Expand Down
6 changes: 6 additions & 0 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const useJWTValidationKey = "useJWTValidation"
const privKeyFileKey = "privKeyFile"
const pubKeyFileKey = "pubKeyFile"
const signingAlgorithmKey = "signingAlgorithm"
const jwksUrlKey = "jwksUrl"
const disableRequestLoggingKey = "disableRequestLogging"

var serveCmd = &cobra.Command{
Expand Down Expand Up @@ -73,6 +74,7 @@ func fetchConfig() {

func fetchJWTValidationConfig() {
jwtValidationOptions = &s.JWTValidationOptions{
JwksUrl: viper.GetString(jwksUrlKey),
PrivKeyFile: viper.GetString(privKeyFileKey),
PubKeyFile: viper.GetString(pubKeyFileKey),
SigningAlgorithm: viper.GetString(signingAlgorithmKey),
Expand Down Expand Up @@ -104,6 +106,10 @@ func init() {
"Provide a valid public key to validate the JWT tokens against")
_ = viper.BindPFlag(signingAlgorithmKey, flags.Lookup(signingAlgorithmKey))

flags.String(jwksUrlKey, "",
"Provide a JWKS Url for automatic JWT validation automation")
_ = viper.BindPFlag(jwksUrlKey, flags.Lookup(jwksUrlKey))

flags.StringVar(&IPRateLimit, "IPRateLimit", "", "rate-limit requests from an IP. e.g. 5-S (5 per second), 1000-H (1000 per hour)")

serveCmd.PersistentFlags().BoolP(disableRequestLoggingKey, "s", false, "disable request logging")
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ module github.com/siemens/link-checker-service
go 1.19

require (
github.com/MicahParks/keyfunc v1.9.0
github.com/appleboy/gin-jwt/v2 v2.9.1
github.com/darren/gpac v0.0.0-20210609082804-b56d6523a3af
github.com/dgraph-io/ristretto v0.1.1
github.com/gin-contrib/cors v1.5.0
github.com/gin-gonic/gin v1.9.1
github.com/go-resty/resty/v2 v2.10.0
github.com/gobwas/glob v0.2.3
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.5.0
github.com/mitchellh/go-homedir v1.1.0
github.com/patrickmn/go-cache v2.1.0+incompatible
Expand Down Expand Up @@ -39,7 +40,6 @@ require (
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/glog v1.2.0 // indirect
github.com/google/pprof v0.0.0-20231212022811-ec68065c825e // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
Expand All @@ -50,7 +50,6 @@ require (
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand Down
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/datadog-go/v5 v5.0.2/go.mod h1:ZI9JFB4ewXbw1sBnF4sxsR2k1H3xjV+PUAOUsHvKpcU=
github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o=
github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/appleboy/gin-jwt/v2 v2.9.1 h1:l29et8iLW6omcHltsOP6LLk4s3v4g2FbFs0koxGWVZs=
Expand Down Expand Up @@ -99,8 +101,7 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
Expand Down Expand Up @@ -169,7 +170,6 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
Expand Down
2 changes: 1 addition & 1 deletion main_test_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"os"
"time"

"github.com/golang-jwt/jwt"
"github.com/golang-jwt/jwt/v4"
)

// adapted the testing technique from
Expand Down
34 changes: 25 additions & 9 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ package server

import (
"context"
"github.com/gin-contrib/cors"
"github.com/gobwas/glob"
"io"
"log"
"math"
Expand All @@ -17,14 +19,13 @@ import (
"sync"
"time"

jwt "github.com/appleboy/gin-jwt/v2"
"github.com/gobwas/glob"
"github.com/MicahParks/keyfunc"
ginGwt "github.com/appleboy/gin-jwt/v2"
jwtv4 "github.com/golang-jwt/jwt/v4"
"github.com/ulule/limiter/v3"
gm "github.com/ulule/limiter/v3/drivers/middleware/gin"
"github.com/ulule/limiter/v3/drivers/store/memory"

"github.com/gin-contrib/cors"

"github.com/gin-gonic/gin"
"github.com/siemens/link-checker-service/infrastructure"
)
Expand All @@ -39,6 +40,7 @@ type JWTValidationOptions struct {
PrivKeyFile string
PubKeyFile string
SigningAlgorithm string
JwksUrl string
}

// Options configures the web service instance
Expand Down Expand Up @@ -141,8 +143,7 @@ func (s *Server) setupRoutes() {
s.setUpRateLimiting(checkURLsRoutes)

if s.options.JWTValidationOptions != nil {
s.setUpJWTValidation(checkURLsRoutes)
s.setUpJWTValidation(statsRoutes)
s.setUpJWTValidation(checkURLsRoutes, statsRoutes)
}

checkURLsRoutes.POST("", s.checkURLs)
Expand Down Expand Up @@ -374,7 +375,7 @@ func (s *Server) isBlacklisted(input URLRequest) bool {
return false
}

func (s *Server) setUpJWTValidation(routerGroup *gin.RouterGroup) {
func (s *Server) setUpJWTValidation(routerGroups ...*gin.RouterGroup) {
if s.options.JWTValidationOptions == nil {
log.Fatal("JWT Validation not set up correctly")
}
Expand All @@ -385,7 +386,8 @@ func (s *Server) setUpJWTValidation(routerGroup *gin.RouterGroup) {
log.Printf(" SigningAlgorithm: %v", s.options.JWTValidationOptions.SigningAlgorithm)

// the jwt middleware
middleware, err := jwt.New(&jwt.GinJWTMiddleware{
middleware, err := ginGwt.New(&ginGwt.GinJWTMiddleware{
KeyFunc: tryGetJwksKeyFunc(s.options.JWTValidationOptions.JwksUrl),
PrivKeyFile: s.options.JWTValidationOptions.PrivKeyFile,
PubKeyFile: s.options.JWTValidationOptions.PubKeyFile,
SigningAlgorithm: s.options.JWTValidationOptions.SigningAlgorithm,
Expand All @@ -399,7 +401,21 @@ func (s *Server) setUpJWTValidation(routerGroup *gin.RouterGroup) {
log.Fatal("JWT Error:" + err.Error())
}

routerGroup.Use(middleware.MiddlewareFunc())
for _, routerGroup := range routerGroups {
routerGroup.Use(middleware.MiddlewareFunc())
}
}

func tryGetJwksKeyFunc(jwksURL string) func(token *jwtv4.Token) (interface{}, error) {
kf, err := keyfunc.Get(jwksURL, keyfunc.Options{
// to do: configurable
RefreshInterval: 1 * time.Hour,
})
if err != nil {
return nil
}
log.Printf("JWKS configured with the url: %s", jwksURL)
return kf.Keyfunc
}

func (s *Server) setUpRateLimiting(routerGroup *gin.RouterGroup) {
Expand Down

0 comments on commit e1c3f37

Please sign in to comment.