Skip to content

Commit 7f487c2

Browse files
TUN-6775: Add middleware.Handler verification to ProxyHTTP
ProxyHTTP now processes middleware Handler before executing the request. A chain of handlers is now executed and appropriate response status codes are sent.
1 parent 9bb7628 commit 7f487c2

File tree

5 files changed

+53
-25
lines changed

5 files changed

+53
-25
lines changed

ingress/middleware/jwtvalidator.go

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ import (
66
"net/http"
77

88
"github.com/coreos/go-oidc/v3/oidc"
9-
"github.com/pkg/errors"
109
)
1110

1211
const (
1312
headerKeyAccessJWTAssertion = "Cf-Access-Jwt-Assertion"
1413
)
1514

1615
var (
17-
ErrNoAccessToken = errors.New("no access token provided in request")
1816
cloudflareAccessCertsURL = "https://%s.cloudflareaccess.com"
1917
)
2018

@@ -39,28 +37,43 @@ func NewJWTValidator(teamName string, certsURL string, audTags []string) *JWTVal
3937
verifier := oidc.NewVerifier(certsURL, keySet, config)
4038
return &JWTValidator{
4139
IDTokenVerifier: verifier,
40+
audTags: audTags,
4241
}
4342
}
4443

45-
func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) error {
44+
func (v *JWTValidator) Name() string {
45+
return "AccessJWTValidator"
46+
}
47+
48+
func (v *JWTValidator) Handle(ctx context.Context, r *http.Request) (*HandleResult, error) {
4649
accessJWT := r.Header.Get(headerKeyAccessJWTAssertion)
4750
if accessJWT == "" {
48-
return ErrNoAccessToken
51+
// log the exact error message here. the message is specific to the handler implementation logic, we don't gain anything
52+
// in passing it upstream. and each handler impl know what logging level to use for each.
53+
return &HandleResult{
54+
ShouldFilterRequest: true,
55+
StatusCode: http.StatusForbidden,
56+
Reason: "no access token in request",
57+
}, nil
4958
}
5059

5160
token, err := v.IDTokenVerifier.Verify(ctx, accessJWT)
5261
if err != nil {
53-
return fmt.Errorf("Invalid token: %w", err)
62+
return nil, err
5463
}
5564

56-
// We want atleast one audTag to match
65+
// We want at least one audTag to match
5766
for _, jwtAudTag := range token.Audience {
5867
for _, acceptedAudTag := range v.audTags {
5968
if acceptedAudTag == jwtAudTag {
60-
return nil
69+
return &HandleResult{ShouldFilterRequest: false}, nil
6170
}
6271
}
6372
}
6473

65-
return fmt.Errorf("Invalid token: %w", err)
74+
return &HandleResult{
75+
ShouldFilterRequest: true,
76+
StatusCode: http.StatusForbidden,
77+
Reason: fmt.Sprintf("Invalid token in jwt: %v", token.Audience),
78+
}, nil
6679
}

ingress/middleware/jwtvalidator_test.go

Lines changed: 0 additions & 6 deletions
This file was deleted.

ingress/middleware/middleware.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import (
55
"net/http"
66
)
77

8+
type HandleResult struct {
9+
// Tells that the request didn't passed the handler and should be filtered
10+
ShouldFilterRequest bool
11+
// The status code to return in case ShouldFilterRequest is true.
12+
StatusCode int
13+
Reason string
14+
}
15+
816
type Handler interface {
9-
Handle(ctx context.Context, r *http.Request) error
17+
Name() string
18+
Handle(ctx context.Context, r *http.Request) (result *HandleResult, err error)
1019
}

ingress/middleware/verifier.go

Lines changed: 0 additions & 10 deletions
This file was deleted.

proxy/proxy.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ func NewOriginProxy(
6060
return proxy
6161
}
6262

63+
func (p *Proxy) applyIngressMiddleware(rule *ingress.Rule, r *http.Request, w connection.ResponseWriter) (error, bool) {
64+
for _, handler := range rule.Handlers {
65+
result, err := handler.Handle(r.Context(), r)
66+
if err != nil {
67+
return errors.Wrap(err, fmt.Sprintf("error while processing middleware handler %s", handler.Name())), false
68+
}
69+
70+
if result.ShouldFilterRequest {
71+
w.WriteRespHeaders(result.StatusCode, nil)
72+
return fmt.Errorf("request filtered by middleware handler (%s) due to: %s", handler.Name(), result.Reason), true
73+
}
74+
}
75+
return nil, true
76+
}
77+
6378
// ProxyHTTP further depends on ingress rules to establish a connection with the origin service. This may be
6479
// a simple roundtrip or a tcp/websocket dial depending on ingres rule setup.
6580
func (p *Proxy) ProxyHTTP(
@@ -86,6 +101,13 @@ func (p *Proxy) ProxyHTTP(
86101
p.logRequest(req, logFields)
87102
ruleSpan.SetAttributes(attribute.Int("rule-num", ruleNum))
88103
ruleSpan.End()
104+
if err, applied := p.applyIngressMiddleware(rule, req, w); err != nil {
105+
if applied {
106+
p.log.Error().Msg(err.Error())
107+
return nil
108+
}
109+
return err
110+
}
89111

90112
switch originProxy := rule.Service.(type) {
91113
case ingress.HTTPOriginProxy:

0 commit comments

Comments
 (0)