mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 20:19:57 +00:00
TUN-6772: Add a JWT Validator as an ingress verifier
This adds a new verifier interface that can be attached to ingress.Rule. This would act as a middleware layer that gets executed at the start of proxy.ProxyHTTP. A jwt validator implementation for this verifier is also provided. The validator downloads the public key from the access teams endpoint and uses it to verify the JWT sent to cloudflared with the audtag (clientID) information provided in the config.
This commit is contained in:
66
ingress/middleware/jwtvalidator.go
Normal file
66
ingress/middleware/jwtvalidator.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
headerKeyAccessJWTAssertion = "Cf-Access-Jwt-Assertion"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoAccessToken = errors.New("no access token provided in request")
|
||||
cloudflareAccessCertsURL = "https://%s.cloudflareaccess.com"
|
||||
)
|
||||
|
||||
// JWTValidator is an implementation of Verifier that validates access based JWT tokens.
|
||||
type JWTValidator struct {
|
||||
*oidc.IDTokenVerifier
|
||||
audTags []string
|
||||
}
|
||||
|
||||
func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTValidator {
|
||||
if certsURL == "" {
|
||||
certsURL = fmt.Sprintf(cloudflareAccessCertsURL, teamName)
|
||||
}
|
||||
certsEndpoint := fmt.Sprintf("%s/cdn-cgi/access/certs", certsURL)
|
||||
|
||||
config := &oidc.Config{
|
||||
SkipClientIDCheck: true,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
keySet := oidc.NewRemoteKeySet(ctx, certsEndpoint)
|
||||
verifier := oidc.NewVerifier(certsURL, keySet, config)
|
||||
return &JWTValidator{
|
||||
IDTokenVerifier: verifier,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *JWTValidator) Handle(ctx context.Context, headers http.Header) error {
|
||||
accessJWT := headers.Get(headerKeyAccessJWTAssertion)
|
||||
if accessJWT == "" {
|
||||
return ErrNoAccessToken
|
||||
}
|
||||
|
||||
token, err := v.IDTokenVerifier.Verify(ctx, accessJWT)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Invalid token: %w", err)
|
||||
}
|
||||
|
||||
// We want atleast one audTag to match
|
||||
for _, jwtAudTag := range token.Audience {
|
||||
for _, acceptedAudTag := range v.audTags {
|
||||
if acceptedAudTag == jwtAudTag {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Invalid token: %w", err)
|
||||
}
|
10
ingress/middleware/middleware.go
Normal file
10
ingress/middleware/middleware.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
Handle(ctx context.Context, r *http.Request) error
|
||||
}
|
@@ -20,6 +20,11 @@ type Rule struct {
|
||||
// address.
|
||||
Service OriginService `json:"service"`
|
||||
|
||||
// Handlers is a list of functions that acts as a middleware during ProxyHTTP
|
||||
// TODO TUN-6774: Uncomment when we parse ingress to this. This serves as a demonstration on how
|
||||
// we want to plug in Verifiers.
|
||||
// Handlers []middleware.Handler
|
||||
|
||||
// Configure the request cloudflared sends to this specific origin.
|
||||
Config OriginRequestConfig `json:"originRequest"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user