mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 19:59:58 +00:00
TUN-6774: Validate OriginRequest.Access to add Ingress.Middleware
We take advantage of the JWTValidator middleware and attach it to an ingress rule based on Access configurations. We attach the Validator directly to the ingress rules because we want to take advantage of caching and token revert/handling that comes with go-oidc.
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/cloudflare/cloudflared/config"
|
||||
"github.com/cloudflare/cloudflared/ingress/middleware"
|
||||
"github.com/cloudflare/cloudflared/ipaccess"
|
||||
)
|
||||
|
||||
@@ -168,6 +169,28 @@ func (ing Ingress) CatchAll() *Rule {
|
||||
return &ing.Rules[len(ing.Rules)-1]
|
||||
}
|
||||
|
||||
func validateAccessConfiguration(cfg *config.AccessConfig) error {
|
||||
if !cfg.Required {
|
||||
return nil
|
||||
}
|
||||
|
||||
// It is possible to set `required:true` and not have these two configured yet.
|
||||
// But if one of them is configured, we'd validate for correctness.
|
||||
if len(cfg.AudTag) == 0 && cfg.TeamName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(cfg.AudTag) == 0 {
|
||||
return errors.New("access audtag cannot be empty")
|
||||
}
|
||||
|
||||
if cfg.TeamName == "" {
|
||||
return errors.New("access.TeamName cannot be blank")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginRequestConfig) (Ingress, error) {
|
||||
rules := make([]Rule, len(ingress))
|
||||
for i, r := range ingress {
|
||||
@@ -237,6 +260,17 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq
|
||||
}
|
||||
}
|
||||
|
||||
var handlers []middleware.Handler
|
||||
if access := r.OriginRequest.Access; access != nil {
|
||||
if err := validateAccessConfiguration(access); err != nil {
|
||||
return Ingress{}, err
|
||||
}
|
||||
if access.Required {
|
||||
verifier := middleware.NewJWTValidator(access.TeamName, "", access.AudTag)
|
||||
handlers = append(handlers, verifier)
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateHostname(r, i, len(ingress)); err != nil {
|
||||
return Ingress{}, err
|
||||
}
|
||||
@@ -255,6 +289,7 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq
|
||||
Hostname: r.Hostname,
|
||||
Service: service,
|
||||
Path: pathRegexp,
|
||||
Handlers: handlers,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
@@ -42,8 +42,8 @@ func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTVal
|
||||
}
|
||||
}
|
||||
|
||||
func (v *JWTValidator) Handle(ctx context.Context, headers http.Header) error {
|
||||
accessJWT := headers.Get(headerKeyAccessJWTAssertion)
|
||||
func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) error {
|
||||
accessJWT := r.Header.Get(headerKeyAccessJWTAssertion)
|
||||
if accessJWT == "" {
|
||||
return ErrNoAccessToken
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/cloudflared/ingress/middleware"
|
||||
)
|
||||
|
||||
// Rule routes traffic from a hostname/path on the public internet to the
|
||||
@@ -21,9 +23,7 @@ type Rule struct {
|
||||
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
|
||||
Handlers []middleware.Handler
|
||||
|
||||
// Configure the request cloudflared sends to this specific origin.
|
||||
Config OriginRequestConfig `json:"originRequest"`
|
||||
|
@@ -182,25 +182,25 @@ func TestMarshalJSON(t *testing.T) {
|
||||
{
|
||||
name: "Nil",
|
||||
path: nil,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Nil regex",
|
||||
path: &Regexp{Regexp: nil},
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`,
|
||||
expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
path: &Regexp{Regexp: regexp.MustCompile("")},
|
||||
expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`,
|
||||
expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Basic",
|
||||
path: &Regexp{Regexp: regexp.MustCompile("/echo")},
|
||||
expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"Enabled":false}}}`,
|
||||
expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
Reference in New Issue
Block a user