Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ type Proxy struct {
// Tips for performance: define your health check endpoint with a different length from the most frequently used endpoint, for example, use `/healthcheck` (len: 12) when `/most_used` (len: 10), instead of `/healthccc` (len: 10)
OriginHealthCheckPaths []string `yaml:"originHealthCheckPaths"`

// NoAuthPaths represents endpoints that requires NO authorization. Wildcard characters supported in Athenz policy are supported too.
NoAuthPaths []string `yaml:"noAuthPaths"`

// PreserveHost represents whether to preserve the host header from the request.
PreserveHost bool `yaml:"preserveHost"`

Expand Down
7 changes: 6 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ func TestNew(t *testing.T) {
Port: 80,
BufferSize: 4096,
OriginHealthCheckPaths: []string{},
PreserveHost: true,
NoAuthPaths: []string{
"/no-auth/any/*",
"/no-auth/single/a?c",
"/no-auth/no-regex/^$|([{",
},
PreserveHost: true,
Transport: Transport{
TLSHandshakeTimeout: 10 * time.Second,
DisableKeepAlives: false,
Expand Down
3 changes: 3 additions & 0 deletions handler/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,7 @@ const (

// ErrRoleTokenNotFound "role token not found"
ErrRoleTokenNotFound = "role token not found"

// ErrInvalidProxyConfig "invalid proxy config".
ErrInvalidProxyConfig = "invalid proxy config"
)
16 changes: 16 additions & 0 deletions handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/kpango/glg"
"github.com/pkg/errors"

"github.com/AthenZ/athenz-authorizer/v5/policy"
"github.com/AthenZ/authorization-proxy/v4/config"
"github.com/AthenZ/authorization-proxy/v4/service"
)
Expand Down Expand Up @@ -89,6 +90,7 @@ func New(cfg config.Proxy, bp httputil.BufferPool, prov service.Authorizationd)
prov: prov,
RoundTripper: transportFromCfg(cfg.Transport),
cfg: cfg,
noAuthPaths: mapPathToAssertion(cfg.NoAuthPaths),
},
ErrorHandler: handleError,
}
Expand Down Expand Up @@ -156,6 +158,20 @@ func transportFromCfg(cfg config.Transport) *http.Transport {
return t
}

func mapPathToAssertion(paths []string) []*policy.Assertion {
as := make([]*policy.Assertion, len(paths))
for i, p := range paths {
var err error
as[i], err = policy.NewAssertion("", ":"+p, "")
if err != nil {
// NewAssertion() escapes all regex characters and should NOT return ANY errors.
glg.Errorf("Invalid proxy.noAuthPaths: %s", p)
panic(ErrInvalidProxyConfig)
}
}
return as
}

func handleError(rw http.ResponseWriter, r *http.Request, err error) {
if r != nil && r.Body != nil {
io.Copy(ioutil.Discard, r.Body)
Expand Down
88 changes: 88 additions & 0 deletions handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"time"

authorizerd "github.com/AthenZ/athenz-authorizer/v5"
"github.com/AthenZ/athenz-authorizer/v5/policy"
"github.com/AthenZ/authorization-proxy/v4/config"
"github.com/AthenZ/authorization-proxy/v4/infra"
"github.com/AthenZ/authorization-proxy/v4/service"
Expand Down Expand Up @@ -625,6 +626,93 @@ func Test_transportFromCfg(t *testing.T) {
}
}

func Test_mapPathToAssertion(t *testing.T) {
type args struct {
paths []string
}
tests := []struct {
name string
args args
want []*policy.Assertion
wantPanic any
}{
{
name: "nil list",
args: args{
paths: nil,
},
want: []*policy.Assertion{},
},
{
name: "empty list",
args: args{
paths: []string{},
},
want: []*policy.Assertion{},
},
{
name: "single assertion",
args: args{
paths: []string{
"/path/656",
},
},
want: func() (as []*policy.Assertion) {
a, err := policy.NewAssertion("", ":/path/656", "")
if err != nil {
panic(err)
}
as = append(as, a)
return as
}(),
},
{
name: "multiple assertion",
args: args{
paths: []string{
"/path/672",
"/path/673",
},
},
want: func() (as []*policy.Assertion) {
a1, err := policy.NewAssertion("", ":/path/672", "")
if err != nil {
panic(err)
}
a2, err := policy.NewAssertion("", ":/path/673", "")
if err != nil {
panic(err)
}
as = append(as, a1, a2)
return as
}(),
},
// {
// name: "invalid assertion",
// args: args{
// paths: []string{
// "no invalid value",
// },
// },
// want: nil,
// wantPanic: ErrInvalidProxyConfig,
// },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
err := recover()
if err != tt.wantPanic {
t.Errorf("mapPathToAssertion() panic = %v, want panic %v", err, tt.wantPanic)
}
}()
if got := mapPathToAssertion(tt.args.paths); !reflect.DeepEqual(got, tt.want) {
t.Errorf("mapPathToAssertion() = %v, want %v", got, tt.want)
}
})
}
}

func Test_handleError(t *testing.T) {
type args struct {
rw http.ResponseWriter
Expand Down
26 changes: 19 additions & 7 deletions handler/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"

authorizerd "github.com/AthenZ/athenz-authorizer/v5"
"github.com/AthenZ/athenz-authorizer/v5/policy"
"github.com/AthenZ/authorization-proxy/v4/config"
"github.com/AthenZ/authorization-proxy/v4/service"

Expand All @@ -32,18 +33,29 @@ import (
type transport struct {
http.RoundTripper

prov service.Authorizationd
cfg config.Proxy
prov service.Authorizationd
cfg config.Proxy
noAuthPaths []*policy.Assertion
}

// Based on the following.
// https://github.com/golang/oauth2/blob/bf48bf16ab8d622ce64ec6ce98d2c98f916b6303/transport.go
func (t *transport) RoundTrip(r *http.Request) (*http.Response, error) {
for _, urlPath := range t.cfg.OriginHealthCheckPaths {
if urlPath == r.URL.Path {
glg.Info("Authorization checking skipped on: " + r.URL.Path)
r.TLS = nil
return t.RoundTripper.RoundTrip(r)
// bypass authoriztion
if len(r.URL.Path) != 0 { // prevent bypassing empty path on default config
for _, urlPath := range t.cfg.OriginHealthCheckPaths {
if urlPath == r.URL.Path {
glg.Info("Authorization checking skipped on: " + r.URL.Path)
r.TLS = nil
return t.RoundTripper.RoundTrip(r)
}
}
for _, ass := range t.noAuthPaths {
if ass.ResourceRegexp.MatchString(strings.ToLower(r.URL.Path)) {
glg.Infof("Authorization checking skipped by %s on: %s", ass.ResourceRegexpString, r.URL.Path)
r.TLS = nil
return t.RoundTripper.RoundTrip(r)
}
}
}

Expand Down
Loading