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:
Sudarsan Reddy
2022-09-21 15:17:44 +01:00
parent e9a2c85671
commit de07da02cd
51 changed files with 4371 additions and 790 deletions

View 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)
}

View File

@@ -0,0 +1,10 @@
package middleware
import (
"context"
"net/http"
)
type Handler interface {
Handle(ctx context.Context, r *http.Request) error
}