Skip to content

Commit

Permalink
Merge pull request #208 from fabiocicerchia/feature/jwt-validation
Browse files Browse the repository at this point in the history
Improvements on JWT Validation PR#171
  • Loading branch information
fabiocicerchia authored Nov 4, 2024
2 parents 274e6b1 + b9cc970 commit 4f5a4d8
Show file tree
Hide file tree
Showing 25 changed files with 3,294 additions and 4,003 deletions.
25 changes: 25 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,28 @@ CACHE_ALLOWED_STATUSES=200,301,302
# though it is recommended to specify them explicitly.
# Default: HEAD,GET
CACHE_ALLOWED_METHODS=HEAD,GET

# --- JWT
# A list of space-separated paths.
# JWT_EXCLUDED_PATHS=/

# A list of space-separated scopes to be allowed.
# JWT_ALLOWED_SCOPES=scope1 scope2

# The JSON Web Key Set (JWKS) URL where it is stored the the public keys used to verify the JSON Web Token (JWT).
# - For global configuration:
# JWT_JWKS_URL=
# - For domain configuration:
# JWT_JWKS_URL_<domain_name_specified_in_config.yml>=
# *e.g: JWT_JWKS_URL_example_com=http://testJwksUrl.com
# Global Configuration - The value of the JwksUrl field is taken by following this preference order:
# - JWT_JWKS_URL enviroment variable
# - global jwks_url variable in config.yaml
# Domain Configuration - The value of the JwksUrl field is taken by following this preference order:
# - JWT_JWKS_URL_<domain_name_specified_in_config.yml> enviroment variable
# - domain jwks_url variable specified in config.yaml
# - global jwks_url variable specified in config.yaml
# - JWT_JWKS_URL enviroment variable

# Time in minutes that takes for JWKS to refresh automatically
# JWT_REFRESH_INTERVAL=15
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ jobs:
run: |
apk update
apk add bash curl gcc git libc-dev make nghttp2 nodejs npm redis openssh musl-dev net-snmp-dev openssl python3
wget https://raw.githubusercontent.com/garabik/grc/refs/heads/master/grc
chmod +x grc
mv grc /usr/local/bin
- name: Build (race)
run: make build-race
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ test/full-setup/certs/*/*.csr
test/full-setup/certs/*/*.key
dist/

go-proxy-cache
go-proxy-cache

cover.html
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,15 @@ tlsfuzzer: ## tlsfuzzer
test: test-unit test-functional test-endtoend test-ws test-http2 ## test

test-unit: ## test unit
TESTING=1 go test -v -race -count=1 --tags=unit ./...
TESTING=1 grc go test -v -race -count=1 --tags=unit ./...

test-functional: ## test functional
python3 -m http.server &> /dev/null &
TESTING=1 go test -v -race -count=1 --tags=functional ./...
TESTING=1 grc go test -v -race -count=1 --tags=functional ./...
pgrep python3 | xargs kill || true

test-endtoend: ## test endtoend
go test -v -race -count=1 --tags=endtoend ./...
grc go test -v -race -count=1 --tags=endtoend ./...

test-ws: ## test websocket
cd test/full-setup/ws && npm install
Expand All @@ -114,9 +114,9 @@ test-http2: ## test HTTP2

cover: ## coverage
python3 -m http.server &> /dev/null &
TESTING=1 go test -race -count=1 --tags=unit,functional -coverprofile c.out ./...
TESTING=1 grc go test -race -count=1 --tags=unit,functional -coverprofile c.out ./...
go tool cover -func=c.out
go tool cover -html=c.out
go tool cover -html=c.out -o cover.html
pgrep python3 | xargs kill || true

codeclimate: ## codeclimate
Expand Down
16 changes: 15 additions & 1 deletion config.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ server:
# --- CACHE
cache:
# --- REDIS SERVER
hosts:
hosts:
- localhost:6379
password: ~
db: 0
Expand Down Expand Up @@ -306,6 +306,20 @@ tracing:
# Default: 1.0
sampling_ratio: 1.0

# --- JWT (careful, setting JWT config here affects all domains)
# jwt:
# # A list of paths.
# excluded_paths:
# - /
# # A list of scopes to be allowed.
# allowed_scopes:
# - scope1
# - scope2
# # The JSON Web Key Set (JWKS) URL where it is stored the the public keys used to verify the JSON Web Token (JWT).
# jwks_url: ~
# # Time in minutes that takes for JWKS to refresh automatically
# jwks_refresh_interval: 15

### PER DOMAIN CONFIGURATION OVERRIDE
################################################################################
domains:
Expand Down
30 changes: 30 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package config
// Repo: https://github.com/fabiocicerchia/go-proxy-cache

import (
"context"
"crypto/tls"
"fmt"
"os"
Expand All @@ -19,6 +20,7 @@ import (

"github.com/jinzhu/copier"
"github.com/kelseyhightower/envconfig"
"github.com/lestrrat-go/jwx/v2/jwk"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"

Expand Down Expand Up @@ -91,13 +93,30 @@ func loadYAMLFilefile(file string) (YamlConfig Configuration) {
return YamlConfig
}

// InitJWT - Configure the jwk auto-refresh and save it into the JWT config
func InitJWT(jwtConfig *Jwt) {
refreshIntervalDuration := time.Duration(jwtConfig.JwksRefreshInterval) * time.Minute
jwtKeyFetcher := jwk.NewCache(jwtConfig.Context, jwk.WithRefreshWindow(refreshIntervalDuration))
jwtKeyFetcher.Register(
jwtConfig.JwksUrl,
jwk.WithMinRefreshInterval(refreshIntervalDuration),
)
jwtConfig.JwkCache = jwtKeyFetcher
}

func copyGlobalOverDomainConfig(file string) {
if Config.Domains != nil {
domains := Config.Domains
for k, v := range domains {
domain := Config
domain.CopyOverWith(v, &file)
domain.Domains = Domains{}
domainName := k
_, isJWKSUrl := os.LookupEnv("JWT_JWKS_URL_" + domainName)
if isJWKSUrl {
domain.Jwt.JwksUrl = os.Getenv("JWT_JWKS_URL_" + domainName)
}
InitJWT(&domain.Jwt)
domains[k] = domain
}

Expand Down Expand Up @@ -137,6 +156,7 @@ func (c *Configuration) CopyOverWith(overrides Configuration, file *string) {
c.copyOverWithCache(overrides.Cache)
c.copyOverWithTracing(overrides.Tracing)
c.copyOverWithLog(overrides.Log)
c.copyOverWithJwt(overrides.Jwt)
}

// --- SERVER.
Expand Down Expand Up @@ -217,6 +237,16 @@ func (c *Configuration) copyOverWithLog(overrides Log) {
c.Log.SyslogEndpoint = utils.Coalesce(overrides.SyslogEndpoint, c.Log.SyslogEndpoint).(string)
}

// --- JWT.
func (c *Configuration) copyOverWithJwt(overrides Jwt) {
c.Jwt.ExcludedPaths = utils.Coalesce(overrides.ExcludedPaths, c.Jwt.ExcludedPaths).([]string)
c.Jwt.AllowedScopes = utils.Coalesce(overrides.AllowedScopes, c.Jwt.AllowedScopes).([]string)
c.Jwt.JwksUrl = utils.Coalesce(overrides.JwksUrl, c.Jwt.JwksUrl).(string)
c.Jwt.JwksRefreshInterval = utils.Coalesce(overrides.JwksRefreshInterval, c.Jwt.JwksRefreshInterval).(int)
c.Jwt.Context = context.Background()
c.Jwt.Logger = log.New()
}

// Print - Shows the current configuration.
func Print() {
obfuscatedConfig := Configuration{}
Expand Down
21 changes: 21 additions & 0 deletions config/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ package config
// Repo: https://github.com/fabiocicerchia/go-proxy-cache

import (
"context"
"crypto/tls"
"net/http"
"time"

"github.com/fabiocicerchia/go-proxy-cache/utils"
circuitbreaker "github.com/fabiocicerchia/go-proxy-cache/utils/circuit-breaker"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/sirupsen/logrus"
)

// DefaultTimeoutRead - Default value used for http.Server.ReadTimeout
Expand Down Expand Up @@ -57,6 +60,7 @@ type Configuration struct {
Log Log `yaml:"log"`
Tracing Tracing `yaml:"tracing"`
domainsCache map[string]Configuration
Jwt Jwt `yaml:"jwt"`
}

// Domains - Overrides per domain.
Expand Down Expand Up @@ -162,6 +166,23 @@ type DomainSet struct {
Scheme string
}

// Jwt - Defines the config for the jwt validation.
type Jwt struct {
ExcludedPaths []string `yaml:"excluded_paths" envconfig:"JWT_EXCLUDED_PATHS" split_words:"true"`
AllowedScopes []string `yaml:"allowed_scopes" envconfig:"JWT_ALLOWED_SCOPES" split_words:"true"`
JwksUrl string `yaml:"jwks_url" envconfig:"JWT_JWKS_URL"`
JwksRefreshInterval int `yaml:"jwks_refresh_interval" envconfig:"JWT_REFRESH_INTERVAL" default:"15"`
JwkCache *jwk.Cache
Context context.Context
Logger *logrus.Logger
}

// Jwt - Defines the jwt validation error.
type JwtError struct {
ErrorCode string `json:"errorCode"`
ErrorDescription string `json:"errorDescription"`
}

// Config - Holds the server configuration.
var Config Configuration = Configuration{
Server: Server{
Expand Down
19 changes: 19 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
- `TLS_KEY_FILE`
- `TRACING_ENABLED`
- `TRACING_JAEGER_ENDPOINT`
- `JWT_EXCLUDED_PATHS`
- `JWT_ALLOWED_SCOPES`
- `JWT_JWKS_URL`
- `JWT_JWKS_URL_<domain_name_specified_in_config.yml>`
- `JWT_REFRESH_INTERVAL`

## YAML

Expand Down Expand Up @@ -353,6 +358,20 @@ tracing:
# Default: 1.0
sampling_ratio: 1.0

# --- JWT
# jwt:
# # A list of paths.
# excluded_paths:
# - /
# # A list of scopes to be allowed.
# allowed_scopes:
# - scope1
# - scope2
# # The JSON Web Key Set (JWKS) URL where it is stored the the public keys used to verify the JSON Web Token (JWT).
# jwks_url: ~
# # Time in minutes that takes for JWKS to refresh automatically
# jwks_refresh_interval: 15

### PER DOMAIN CONFIGURATION OVERRIDE
################################################################################
domains:
Expand Down
1 change: 1 addition & 0 deletions docs/DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Go v1.15
- make
- [wrk](https://github.com/wg/wrk)
- [grc](https://github.com/garabik/grc)

## Setup

Expand Down
43 changes: 25 additions & 18 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ require (
github.com/go-http-utils/fresh v0.0.0-20161124030543-7231e26a4b27
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a
github.com/go-redis/redis/v8 v8.11.5
github.com/go-redsync/redsync/v4 v4.10.0
github.com/gorilla/websocket v1.5.0
github.com/go-redsync/redsync/v4 v4.12.1
github.com/gorilla/websocket v1.5.1
github.com/jinzhu/copier v0.4.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/lestrrat-go/jwx/v2 v2.0.21
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_golang v1.19.0
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529
github.com/rs/xid v1.5.0
github.com/sirupsen/logrus v1.9.3
github.com/sony/gobreaker v0.5.0
github.com/stretchr/testify v1.8.4
github.com/ugorji/go/codec v1.2.11
github.com/stretchr/testify v1.9.0
github.com/ugorji/go/codec v1.2.12
github.com/yhat/wsutil v0.0.0-20170731153501-1d66fa95c997
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/sdk v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
go.opentelemetry.io/otel/sdk v1.24.0
go.opentelemetry.io/otel/trace v1.24.0
golang.org/x/crypto v0.21.0
golang.org/x/net v0.23.0
gopkg.in/yaml.v2 v2.4.0
Expand All @@ -35,22 +36,28 @@ require (
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/getsentry/raven-go v0.2.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.5 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
golang.org/x/sync v0.3.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
Expand Down
Loading

0 comments on commit 4f5a4d8

Please sign in to comment.