Skip to content

Commit

Permalink
add oidc plugin (#301)
Browse files Browse the repository at this point in the history
Signed-off-by: spacewander <[email protected]>
  • Loading branch information
spacewander authored Feb 21, 2024
1 parent 1d17d3f commit cb88689
Show file tree
Hide file tree
Showing 19 changed files with 1,339 additions and 27 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ run-demo:
-v $(PWD)/libgolang.so:/etc/libgolang.so \
-p 10000:10000 \
${PROXY_IMAGE} \
envoy -c /etc/demo.yaml --log-level debug
envoy -c /etc/demo.yaml --log-level info

.PHONY: dev-tools
dev-tools:
Expand Down Expand Up @@ -242,7 +242,7 @@ verify-example:

.PHONY: start-service
start-service:
cd ./plugins/tests/integration/testdata/services && docker-compose up -d
cd ./plugins/tests/integration/testdata/services && docker-compose up -d --build

# E2E
KUBECTL ?= $(LOCALBIN)/kubectl
Expand Down
8 changes: 6 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/agiledragon/gomonkey/v2 v2.11.0
github.com/casbin/casbin/v2 v2.82.0
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101
github.com/coreos/go-oidc/v3 v3.9.0
github.com/envoyproxy/envoy v1.29.1-0.20240208055117-b788e1a92347
github.com/envoyproxy/go-control-plane v0.11.2-0.20231019082134-6e4589f570e1 // version used by istio 1.20
github.com/envoyproxy/protoc-gen-validate v1.0.2
Expand All @@ -19,6 +20,7 @@ require (
github.com/golang/protobuf v1.5.3
github.com/google/cel-go v0.20.0
github.com/google/uuid v1.6.0
github.com/gorilla/securecookie v1.1.2
github.com/jellydator/ttlcache/v3 v3.1.1
github.com/nacos-group/nacos-sdk-go v1.1.4
github.com/onsi/ginkgo/v2 v2.15.0
Expand All @@ -29,6 +31,8 @@ require (
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
go.uber.org/zap v1.26.0
golang.org/x/net v0.19.0
golang.org/x/oauth2 v0.15.0
golang.org/x/text v0.14.0
golang.org/x/time v0.5.0
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17
Expand Down Expand Up @@ -63,6 +67,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
Expand Down Expand Up @@ -114,9 +119,8 @@ require (
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/term v0.15.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -84,6 +86,8 @@ github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
Expand Down Expand Up @@ -132,6 +136,7 @@ github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
Expand All @@ -147,6 +152,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
Expand Down Expand Up @@ -306,9 +313,12 @@ go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
Expand Down
36 changes: 18 additions & 18 deletions pkg/filtermanager/filtermanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type FilterManagerConfig struct {
}

type filterManagerConfig struct {
authnFiltersEndAt int
consumerFiltersEndAt int

current []*model.ParsedFilterConfig
pool *sync.Pool
Expand Down Expand Up @@ -110,7 +110,7 @@ func (p *FilterManagerConfigParser) Parse(any *anypb.Any, callbacks capi.ConfigC
conf := initFilterManagerConfig(fmConfig.Namespace)
conf.current = make([]*model.ParsedFilterConfig, 0, len(plugins))

authnFiltersEndAt := 0
consumerFiltersEndAt := 0
i := 0

for _, proto := range plugins {
Expand All @@ -119,7 +119,7 @@ func (p *FilterManagerConfigParser) Parse(any *anypb.Any, callbacks capi.ConfigC
// For now, we have nothing to provide as config callbacks
config, err := plugin.ConfigParser.Parse(proto.Config, nil)
if err != nil {
api.LogErrorf("%w during parsing plugin %s in filtermanager", err, name)
api.LogErrorf("%s during parsing plugin %s in filtermanager", err, name)

// Return an error from the Parse method will cause assertion failure.
// See https://github.com/envoyproxy/envoy/blob/f301eebf7acc680e27e03396a1be6be77e1ae3a5/contrib/golang/filters/http/source/golang_filter.cc#L1736-L1737
Expand All @@ -137,9 +137,9 @@ func (p *FilterManagerConfigParser) Parse(any *anypb.Any, callbacks capi.ConfigC
Factory: plugin.Factory,
})

p := pkgPlugins.LoadHttpPlugin(name)
if p.Order().Position == pkgPlugins.OrderPositionAuthn {
authnFiltersEndAt = i + 1
_, ok := pkgPlugins.LoadHttpPlugin(name).(pkgPlugins.ConsumerPlugin)
if ok {
consumerFiltersEndAt = i + 1
}
}
i++
Expand All @@ -148,7 +148,7 @@ func (p *FilterManagerConfigParser) Parse(any *anypb.Any, callbacks capi.ConfigC
api.LogErrorf("plugin %s not found, ignored", name)
}
}
conf.authnFiltersEndAt = authnFiltersEndAt
conf.consumerFiltersEndAt = consumerFiltersEndAt

return conf, nil
}
Expand Down Expand Up @@ -181,8 +181,8 @@ func newFilterWrapper(name string, f api.Filter) *filterWrapper {
}

type filterManager struct {
filters []*filterWrapper
authnFilters []*filterWrapper
filters []*filterWrapper
consumerFilters []*filterWrapper

decodeRequestNeeded bool
decodeIdx int
Expand All @@ -208,7 +208,7 @@ type filterManager struct {

func (m *filterManager) Reset() {
m.filters = nil
m.authnFilters = nil
m.consumerFilters = nil

m.decodeRequestNeeded = false
m.decodeIdx = -1
Expand Down Expand Up @@ -373,11 +373,11 @@ func FilterManagerFactory(c interface{}, cb capi.FilterCallbackHandler) capi.Str

fm.filters = filters

if conf.authnFiltersEndAt != 0 {
authnFiltersEndAt := conf.authnFiltersEndAt
authnFilters := filters[:authnFiltersEndAt]
fm.authnFilters = authnFilters
fm.filters = filters[authnFiltersEndAt:]
if conf.consumerFiltersEndAt != 0 {
consumerFiltersEndAt := conf.consumerFiltersEndAt
consumerFilters := filters[:consumerFiltersEndAt]
fm.consumerFilters = consumerFilters
fm.filters = filters[consumerFiltersEndAt:]
}

// The skip check is based on the compiled code. So if the DecodeRequest is defined,
Expand Down Expand Up @@ -474,9 +474,9 @@ func (m *filterManager) DecodeHeaders(headers api.RequestHeaderMap, endStream bo
var res api.ResultAction

m.reqHdr = headers
if len(m.authnFilters) > 0 {
for _, f := range m.authnFilters {
// Authn plugins only use DecodeHeaders for now
if len(m.consumerFilters) > 0 {
for _, f := range m.consumerFilters {
// Consumer plugins only use DecodeHeaders for now
res = f.DecodeHeaders(headers, endStream)
if m.handleAction(res, phaseDecodeHeaders) {
return
Expand Down
2 changes: 1 addition & 1 deletion pkg/filtermanager/filtermanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func (f *addReqFilter) DecodeHeaders(headers api.RequestHeaderMap, endStream boo
func TestFiltersFromConsumer(t *testing.T) {
cb := envoy.NewCAPIFilterCallbackHandler()
config := initFilterManagerConfig("ns")
config.authnFiltersEndAt = 1
config.consumerFiltersEndAt = 1
config.current = []*model.ParsedFilterConfig{
{
Name: "set_consumer",
Expand Down
71 changes: 69 additions & 2 deletions pkg/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ package request

import (
"fmt"
"net/http"
"net/textproto"
"net/url"
"strings"

"github.com/envoyproxy/envoy/contrib/golang/common/go/api"
"golang.org/x/net/http/httpguts"
)

func GetUrl(header api.RequestHeaderMap) *url.URL {
path := header.Path()
func GetUrl(headers api.RequestHeaderMap) *url.URL {
path := headers.Path()
// TODO: cache it
uri, err := url.ParseRequestURI(path)
if err != nil {
Expand All @@ -31,6 +35,69 @@ func GetUrl(header api.RequestHeaderMap) *url.URL {
return uri
}

// The cookie parser is from Go's http/cookie.go, which are not exported

func isNotToken(r rune) bool {
return !httpguts.IsTokenRune(r)
}

func isCookieNameValid(raw string) bool {
if raw == "" {
return false
}
return strings.IndexFunc(raw, isNotToken) < 0
}

func validCookieValueByte(b byte) bool {
return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
}

func parseCookieValue(raw string, allowDoubleQuote bool) (string, bool) {
// Strip the quotes, if present.
if allowDoubleQuote && len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
raw = raw[1 : len(raw)-1]
}
for i := 0; i < len(raw); i++ {
if !validCookieValueByte(raw[i]) {
return "", false
}
}
return raw, true
}

// If multiple cookies match the given name, only one cookie will be returned.
func GetCookies(headers api.RequestHeaderMap) map[string]*http.Cookie {
lines := headers.Values("Cookie")
if len(lines) == 0 {
return map[string]*http.Cookie{}
}

cookies := make(map[string]*http.Cookie, len(lines)+strings.Count(lines[0], ";"))
for _, line := range lines {
line = textproto.TrimString(line)

var part string
for len(line) > 0 { // continue since we have rest
part, line, _ = strings.Cut(line, ";")
part = textproto.TrimString(part)
if part == "" {
continue
}
name, val, _ := strings.Cut(part, "=")
name = textproto.TrimString(name)
if !isCookieNameValid(name) {
continue
}
val, ok := parseCookieValue(val, true)
if !ok {
continue
}
cookies[name] = &http.Cookie{Name: name, Value: val}
}
}
return cookies
}

// GetHeaders returns a plain map represents the headers. The returned headers won't
// contain any pseudo header like `:authority`.
func GetHeaders(header api.RequestHeaderMap) map[string][]string {
Expand Down
Loading

0 comments on commit cb88689

Please sign in to comment.